diff options
Diffstat (limited to 'test/syscalls/linux')
295 files changed, 0 insertions, 91029 deletions
diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc deleted file mode 100644 index 3c825477c..000000000 --- a/test/syscalls/linux/32bit.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <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" - -#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()); - - // Copy in the actual instruction. - 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"); -} - -constexpr int kExitCode = 42; - -TEST(Syscall32Bit, Int80) { - switch (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - case PlatformSupport::Segfault: - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), - ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Ignored: - // Since the call is ignored, we'll hit the int3 trap. - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), - ::testing::KilledBySignal(SIGTRAP), ""); - break; - - case PlatformSupport::Allowed: - EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42), - ""); - break; - } -} - -TEST(Syscall32Bit, Sysenter) { - if ((PlatformSupport32Bit() == PlatformSupport::Allowed || - PlatformSupport32Bit() == PlatformSupport::Ignored) && - GetCPUVendor() == CPUVendor::kAMD) { - // SYSENTER is an illegal instruction in compatibility mode on AMD. - EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), - ::testing::KilledBySignal(SIGILL), ""); - return; - } - - switch (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - - 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; - } -} - -TEST(Syscall32Bit, Syscall) { - if ((PlatformSupport32Bit() == PlatformSupport::Allowed || - PlatformSupport32Bit() == PlatformSupport::Ignored) && - GetCPUVendor() == CPUVendor::kIntel) { - // SYSCALL is an illegal instruction in compatibility mode on Intel. - EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), - ::testing::KilledBySignal(SIGILL), ""); - return; - } - - 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(SIGSEGV), ""); - break; - - case PlatformSupport::Allowed: - EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), - ::testing::ExitedWithCode(42), ""); - break; - } -} - -// 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 (PlatformSupport32Bit()) { - case PlatformSupport::NotSupported: - break; - - case PlatformSupport::Segfault: - EXPECT_EXIT(FarCall32(), ::testing::KilledBySignal(SIGSEGV), ""); - break; - - case PlatformSupport::Ignored: - ABSL_FALLTHROUGH_INTENDED; - case PlatformSupport::Allowed: - // Shouldn't crash. - FarCall32(); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD deleted file mode 100644 index 043ada583..000000000 --- a/test/syscalls/linux/BUILD +++ /dev/null @@ -1,4207 +0,0 @@ -load("//tools:defs.bzl", "cc_binary", "cc_library", "default_net_util", "gtest", "select_arch", "select_system") - -package( - default_visibility = ["//:sandbox"], - licenses = ["notice"], -) - -exports_files( - [ - "socket.cc", - "socket_inet_loopback.cc", - "socket_ip_loopback_blocking.cc", - "socket_ip_tcp_generic_loopback.cc", - "socket_ip_tcp_loopback.cc", - "socket_ip_tcp_loopback_blocking.cc", - "socket_ip_tcp_loopback_nonblock.cc", - "socket_ip_tcp_udp_generic.cc", - "socket_ip_udp_loopback.cc", - "socket_ip_udp_loopback_blocking.cc", - "socket_ip_udp_loopback_nonblock.cc", - "socket_ip_unbound.cc", - "socket_ipv4_udp_unbound_external_networking_test.cc", - "socket_ipv6_udp_unbound_external_networking_test.cc", - "socket_ipv4_udp_unbound_loopback.cc", - "socket_ipv6_udp_unbound_loopback.cc", - "socket_ipv4_udp_unbound_loopback_nogotsan.cc", - "tcp_socket.cc", - "udp_bind.cc", - "udp_socket.cc", - ], - visibility = ["//:sandbox"], -) - -cc_binary( - name = "sigaltstack_check", - testonly = 1, - srcs = ["sigaltstack_check.cc"], - deps = ["//test/util:logging"], -) - -cc_binary( - name = "exec_assert_closed_workload", - testonly = 1, - srcs = ["exec_assert_closed_workload.cc"], - deps = [ - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "exec_basic_workload", - testonly = 1, - srcs = [ - "exec.h", - "exec_basic_workload.cc", - ], -) - -cc_binary( - name = "exec_proc_exe_workload", - testonly = 1, - srcs = ["exec_proc_exe_workload.cc"], - deps = [ - "//test/util:fs_util", - "//test/util:posix_error", - ], -) - -cc_binary( - name = "exec_state_workload", - testonly = 1, - srcs = ["exec_state_workload.cc"], - deps = ["@com_google_absl//absl/strings"], -) - -sh_binary( - name = "exit_script", - testonly = 1, - srcs = [ - "exit_script.sh", - ], -) - -cc_binary( - name = "priority_execve", - testonly = 1, - srcs = [ - "priority_execve.cc", - ], -) - -cc_library( - name = "base_poll_test", - testonly = 1, - srcs = ["base_poll_test.cc"], - hdrs = ["base_poll_test.h"], - deps = [ - "@com_google_absl//absl/memory", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:signal_util", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_library( - name = "file_base", - testonly = 1, - hdrs = ["file_base.h"], - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_util", - ], -) - -cc_library( - name = "socket_netlink_util", - testonly = 1, - srcs = ["socket_netlink_util.cc"], - hdrs = ["socket_netlink_util.h"], - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - "//test/util:posix_error", - "@com_google_absl//absl/strings", - ], -) - -cc_library( - name = "socket_netlink_route_util", - testonly = 1, - srcs = ["socket_netlink_route_util.cc"], - hdrs = ["socket_netlink_route_util.h"], - deps = [ - ":socket_netlink_util", - ], -) - -cc_library( - name = "socket_test_util", - testonly = 1, - srcs = [ - "socket_test_util.cc", - "socket_test_util_impl.cc", - ], - hdrs = ["socket_test_util.h"], - defines = select_system(), - deps = default_net_util() + [ - gtest, - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", - "//test/util:file_descriptor", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_library( - name = "unix_domain_socket_test_util", - testonly = 1, - srcs = ["unix_domain_socket_test_util.cc"], - hdrs = ["unix_domain_socket_test_util.h"], - deps = [ - ":socket_test_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_util", - ], -) - -cc_library( - name = "ip_socket_test_util", - testonly = 1, - srcs = ["ip_socket_test_util.cc"], - hdrs = ["ip_socket_test_util.h"], - deps = [ - ":socket_test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "clock_nanosleep_test", - testonly = 1, - srcs = ["clock_nanosleep.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "@com_google_absl//absl/time", - gtest, - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "32bit_test", - testonly = 1, - srcs = select_arch( - amd64 = ["32bit.cc"], - arm64 = [], - ), - linkstatic = 1, - deps = [ - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:memory_util", - "//test/util:platform_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "accept_bind_test", - testonly = 1, - srcs = ["accept_bind.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "accept_bind_stream_test", - testonly = 1, - srcs = ["accept_bind_stream.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "access_test", - testonly = 1, - srcs = ["access.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:fs_util", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "affinity_test", - testonly = 1, - srcs = ["affinity.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "aio_test", - testonly = 1, - srcs = [ - "aio.cc", - "file_base.h", - ], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:memory_util", - "//test/util:posix_error", - "//test/util:proc_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "alarm_test", - testonly = 1, - srcs = ["alarm.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:signal_util", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "bad_test", - testonly = 1, - srcs = ["bad.cc"], - linkstatic = 1, - visibility = [ - "//:sandbox", - ], - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "bind_test", - testonly = 1, - srcs = ["bind.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_test", - testonly = 1, - srcs = ["socket.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - gtest, - "//test/util:file_descriptor", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_capability_test", - testonly = 1, - srcs = ["socket_capability.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "brk_test", - testonly = 1, - srcs = ["brk.cc"], - linkstatic = 1, - deps = [ - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "chdir_test", - testonly = 1, - srcs = ["chdir.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "chmod_test", - testonly = 1, - srcs = ["chmod.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "chown_test", - testonly = 1, - srcs = ["chown.cc"], - linkstatic = 1, - # We require additional UIDs for this test, so don't include the bazel - # sandbox as standard. - tags = ["no-sandbox"], - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/synchronization", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "sticky_test", - testonly = 1, - srcs = ["sticky.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/flags:flag", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "chroot_test", - testonly = 1, - srcs = ["chroot.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:logging", - "//test/util:mount_util", - "//test/util:multiprocess_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "clock_getres_test", - testonly = 1, - srcs = ["clock_getres.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "clock_gettime_test", - testonly = 1, - srcs = ["clock_gettime.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "concurrency_test", - testonly = 1, - srcs = ["concurrency.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:platform_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "connect_external_test", - testonly = 1, - srcs = ["connect_external.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "creat_test", - testonly = 1, - srcs = ["creat.cc"], - linkstatic = 1, - deps = [ - "//test/util:fs_util", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "dev_test", - testonly = 1, - srcs = ["dev.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "dup_test", - testonly = 1, - srcs = ["dup.cc"], - linkstatic = 1, - deps = [ - "//test/util:eventfd_util", - "//test/util:file_descriptor", - gtest, - "//test/util:fs_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "epoll_test", - testonly = 1, - srcs = ["epoll.cc"], - linkstatic = 1, - deps = [ - "//test/util:epoll_util", - "//test/util:eventfd_util", - "//test/util:file_descriptor", - gtest, - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "eventfd_test", - testonly = 1, - srcs = ["eventfd.cc"], - linkstatic = 1, - deps = [ - "//test/util:epoll_util", - "//test/util:eventfd_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "exceptions_test", - testonly = 1, - srcs = ["exceptions.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:logging", - "//test/util:platform_util", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "getcpu_test", - testonly = 1, - srcs = ["getcpu.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "getcpu_host_test", - testonly = 1, - srcs = ["getcpu.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "getrusage_test", - testonly = 1, - srcs = ["getrusage.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "exec_binary_test", - testonly = 1, - srcs = ["exec_binary.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:proc_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "exec_test", - testonly = 1, - srcs = [ - "exec.cc", - "exec.h", - ], - data = [ - ":exec_assert_closed_workload", - ":exec_basic_workload", - ":exec_proc_exe_workload", - ":exec_state_workload", - ":exit_script", - ":priority_execve", - ], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/types:optional", - gtest, - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "exit_test", - testonly = 1, - srcs = ["exit.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:time_util", - ], -) - -cc_binary( - name = "fallocate_test", - testonly = 1, - srcs = ["fallocate.cc"], - linkstatic = 1, - deps = [ - ":file_base", - ":socket_test_util", - "//test/util:cleanup", - "//test/util:eventfd_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "fault_test", - testonly = 1, - srcs = ["fault.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "fchdir_test", - testonly = 1, - srcs = ["fchdir.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "fcntl_test", - testonly = 1, - srcs = ["fcntl.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:eventfd_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:signal_util", - "//test/util:temp_path", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "flock_test", - testonly = 1, - srcs = [ - "file_base.h", - "flock.cc", - ], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:epoll_util", - "//test/util:eventfd_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "fork_test", - testonly = 1, - srcs = ["fork.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "fpsig_fork_test", - testonly = 1, - srcs = ["fpsig_fork.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "fpsig_nested_test", - testonly = 1, - srcs = ["fpsig_nested.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "sync_file_range_test", - testonly = 1, - srcs = ["sync_file_range.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "fsync_test", - testonly = 1, - srcs = ["fsync.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "futex_test", - testonly = 1, - srcs = ["futex.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:file_descriptor", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/time", - gtest, - "//test/util:memory_util", - "//test/util:save_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:time_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "getdents_test", - testonly = 1, - srcs = ["getdents.cc"], - linkstatic = 1, - deps = [ - "//test/util:eventfd_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/container:node_hash_map", - "@com_google_absl//absl/container:node_hash_set", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "getrandom_test", - testonly = 1, - srcs = ["getrandom.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "inotify_test", - testonly = 1, - srcs = ["inotify.cc"], - linkstatic = 1, - deps = [ - "//test/util:epoll_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "ioctl_test", - testonly = 1, - srcs = ["ioctl.cc"], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_library( - name = "iptables_types", - testonly = 1, - hdrs = [ - "iptables.h", - ], -) - -cc_binary( - name = "iptables_test", - testonly = 1, - srcs = [ - "iptables.cc", - ], - linkstatic = 1, - deps = [ - ":iptables_types", - ":socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "ip6tables_test", - testonly = 1, - srcs = [ - "ip6tables.cc", - ], - linkstatic = 1, - deps = [ - ":iptables_types", - ":socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "itimer_test", - testonly = 1, - srcs = ["itimer.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "kcov_test", - testonly = 1, - srcs = ["kcov.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "kill_test", - testonly = 1, - srcs = ["kill.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "link_test", - testonly = 1, - srcs = ["link.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "lseek_test", - testonly = 1, - srcs = ["lseek.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "madvise_test", - testonly = 1, - srcs = ["madvise.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "membarrier_test", - testonly = 1, - srcs = ["membarrier.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/time", - gtest, - "//test/util:cleanup", - "//test/util:logging", - "//test/util:memory_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "mempolicy_test", - testonly = 1, - srcs = ["mempolicy.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "@com_google_absl//absl/memory", - gtest, - "//test/util:memory_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "mincore_test", - testonly = 1, - srcs = ["mincore.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:memory_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "mkdir_test", - testonly = 1, - srcs = ["mkdir.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:fs_util", - gtest, - "//test/util:temp_path", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "mknod_test", - testonly = 1, - srcs = ["mknod.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "mlock_test", - testonly = 1, - srcs = ["mlock.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - gtest, - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:rlimit_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "mmap_test", - testonly = 1, - srcs = ["mmap.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "mount_test", - testonly = 1, - srcs = ["mount.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:mount_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "mremap_test", - testonly = 1, - srcs = ["mremap.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "msync_test", - testonly = 1, - srcs = ["msync.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:memory_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "munmap_test", - testonly = 1, - srcs = ["munmap.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "open_test", - testonly = 1, - srcs = [ - "file_base.h", - "open.cc", - ], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "open_create_test", - testonly = 1, - srcs = ["open_create.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:temp_umask", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "packet_socket_raw_test", - testonly = 1, - srcs = ["packet_socket_raw.cc"], - defines = select_system(), - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/base:endian", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "packet_socket_test", - testonly = 1, - srcs = ["packet_socket.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/base:endian", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "pty_test", - testonly = 1, - srcs = ["pty.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:cleanup", - "//test/util:posix_error", - "//test/util:pty_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "pty_root_test", - testonly = 1, - srcs = ["pty_root.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:posix_error", - "//test/util:pty_util", - "//test/util:test_main", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "partial_bad_buffer_test", - testonly = 1, - srcs = ["partial_bad_buffer.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "pause_test", - testonly = 1, - srcs = ["pause.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "ping_socket_test", - testonly = 1, - srcs = ["ping_socket.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:save_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "pipe_test", - testonly = 1, - srcs = ["pipe.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "poll_test", - testonly = 1, - srcs = ["poll.cc"], - linkstatic = 1, - deps = [ - ":base_poll_test", - "//test/util:eventfd_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "ppoll_test", - testonly = 1, - srcs = ["ppoll.cc"], - linkstatic = 1, - deps = [ - ":base_poll_test", - "@com_google_absl//absl/time", - gtest, - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "arch_prctl_test", - testonly = 1, - srcs = select_arch( - amd64 = ["arch_prctl.cc"], - arm64 = [], - ), - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "prctl_test", - testonly = 1, - srcs = ["prctl.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "@com_google_absl//absl/flags:flag", - gtest, - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "prctl_setuid_test", - testonly = 1, - srcs = ["prctl_setuid.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "@com_google_absl//absl/flags:flag", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "pread64_test", - testonly = 1, - srcs = ["pread64.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "preadv_test", - testonly = 1, - srcs = ["preadv.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "preadv2_test", - testonly = 1, - srcs = [ - "file_base.h", - "preadv2.cc", - ], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "priority_test", - testonly = 1, - srcs = ["priority.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "proc_test", - testonly = 1, - srcs = ["proc.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/container:node_hash_set", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:proc_util", - "//test/util:temp_path", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:time_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "proc_net_test", - testonly = 1, - srcs = ["proc_net.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "proc_pid_oomscore_test", - testonly = 1, - srcs = ["proc_pid_oomscore.cc"], - linkstatic = 1, - deps = [ - "//test/util:fs_util", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "proc_pid_smaps_test", - testonly = 1, - srcs = ["proc_pid_smaps.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/types:optional", - gtest, - "//test/util:memory_util", - "//test/util:posix_error", - "//test/util:proc_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "proc_pid_uid_gid_map_test", - testonly = 1, - srcs = ["proc_pid_uid_gid_map.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:time_util", - ], -) - -cc_binary( - name = "pselect_test", - testonly = 1, - srcs = ["pselect.cc"], - linkstatic = 1, - deps = [ - ":base_poll_test", - "@com_google_absl//absl/time", - gtest, - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "ptrace_test", - testonly = 1, - srcs = ["ptrace.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/time", - gtest, - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "//test/util:logging", - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:platform_util", - "//test/util:signal_util", - "//test/util:temp_path", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:time_util", - ], -) - -cc_binary( - name = "pwrite64_test", - testonly = 1, - srcs = ["pwrite64.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "pwritev2_test", - testonly = 1, - srcs = [ - "pwritev2.cc", - ], - linkstatic = 1, - deps = [ - ":file_base", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "raw_socket_hdrincl_test", - testonly = 1, - srcs = ["raw_socket_hdrincl.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/base:endian", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "raw_socket_test", - testonly = 1, - srcs = ["raw_socket.cc"], - defines = select_system(), - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "raw_socket_icmp_test", - testonly = 1, - srcs = ["raw_socket_icmp.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "read_test", - testonly = 1, - srcs = ["read.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:cleanup", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "readahead_test", - testonly = 1, - srcs = ["readahead.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "readv_test", - testonly = 1, - srcs = [ - "file_base.h", - "readv.cc", - "readv_common.cc", - "readv_common.h", - ], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "readv_socket_test", - testonly = 1, - srcs = [ - "readv_common.cc", - "readv_common.h", - "readv_socket.cc", - ], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "rename_test", - testonly = 1, - srcs = ["rename.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "rlimits_test", - testonly = 1, - srcs = ["rlimits.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "rseq_test", - testonly = 1, - srcs = ["rseq.cc"], - data = ["//test/syscalls/linux/rseq"], - linkstatic = 1, - deps = [ - "//test/syscalls/linux/rseq:lib", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "rtsignal_test", - testonly = 1, - srcs = ["rtsignal.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - gtest, - "//test/util:logging", - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sched_test", - testonly = 1, - srcs = ["sched.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sched_yield_test", - testonly = 1, - srcs = ["sched_yield.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "seccomp_test", - testonly = 1, - srcs = ["seccomp.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:proc_util", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "select_test", - testonly = 1, - srcs = ["select.cc"], - linkstatic = 1, - deps = [ - ":base_poll_test", - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:rlimit_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sendfile_test", - testonly = 1, - srcs = ["sendfile.cc"], - linkstatic = 1, - deps = [ - "//test/util:eventfd_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:signal_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "sendfile_socket_test", - testonly = 1, - srcs = ["sendfile_socket.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - ":ip_socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "setgid_test", - testonly = 1, - srcs = ["setgid.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:fs_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/strings", - gtest, - ], -) - -cc_binary( - name = "splice_test", - testonly = 1, - srcs = ["splice.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:signal_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "sigaction_test", - testonly = 1, - srcs = ["sigaction.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sigaltstack_test", - testonly = 1, - srcs = ["sigaltstack.cc"], - data = [ - ":sigaltstack_check", - ], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:fs_util", - gtest, - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "sigreturn_test", - testonly = 1, - srcs = select_arch( - amd64 = ["sigreturn_amd64.cc"], - arm64 = ["sigreturn_arm64.cc"], - ), - linkstatic = 1, - deps = [ - gtest, - "//test/util:logging", - "//test/util:signal_util", - "//test/util:test_util", - "//test/util:timer_util", - ] + select_arch( - amd64 = [], - arm64 = ["//test/util:test_main"], - ), -) - -cc_binary( - name = "signalfd_test", - testonly = 1, - srcs = ["signalfd.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/synchronization", - gtest, - "//test/util:logging", - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "sigprocmask_test", - testonly = 1, - srcs = ["sigprocmask.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sigstop_test", - testonly = 1, - srcs = ["sigstop.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/time", - gtest, - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "sigtimedwait_test", - testonly = 1, - srcs = ["sigtimedwait.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:signal_util", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_library( - name = "socket_generic_test_cases", - testonly = 1, - srcs = [ - "socket_generic_test_cases.cc", - ], - hdrs = [ - "socket_generic.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_binary( - name = "socket_stress_test", - testonly = 1, - srcs = [ - "socket_generic_stress.cc", - ], - linkstatic = 1, - deps = [ - gtest, - ":ip_socket_test_util", - ":socket_test_util", - "//test/util:file_descriptor", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - ], -) - -cc_library( - name = "socket_unix_dgram_test_cases", - testonly = 1, - srcs = ["socket_unix_dgram.cc"], - hdrs = ["socket_unix_dgram.h"], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_unix_seqpacket_test_cases", - testonly = 1, - srcs = ["socket_unix_seqpacket.cc"], - hdrs = ["socket_unix_seqpacket.h"], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ip_tcp_generic_test_cases", - testonly = 1, - srcs = [ - "socket_ip_tcp_generic.cc", - ], - hdrs = [ - "socket_ip_tcp_generic.h", - ], - deps = [ - ":socket_test_util", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/time", - gtest, - "//test/util:temp_path", - "//test/util:test_util", - "//test/util:thread_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_non_blocking_test_cases", - testonly = 1, - srcs = [ - "socket_non_blocking.cc", - ], - hdrs = [ - "socket_non_blocking.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_unix_non_stream_test_cases", - testonly = 1, - srcs = [ - "socket_unix_non_stream.cc", - ], - hdrs = [ - "socket_unix_non_stream.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:memory_util", - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_non_stream_test_cases", - testonly = 1, - srcs = [ - "socket_non_stream.cc", - ], - hdrs = [ - "socket_non_stream.h", - ], - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ip_udp_test_cases", - testonly = 1, - srcs = [ - "socket_ip_udp_generic.cc", - ], - hdrs = [ - "socket_ip_udp_generic.h", - ], - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ipv4_udp_unbound_test_cases", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound.cc", - ], - hdrs = [ - "socket_ipv4_udp_unbound.h", - ], - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - "@com_google_absl//absl/memory", - gtest, - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ipv6_udp_unbound_test_cases", - testonly = 1, - srcs = [ - "socket_ipv6_udp_unbound.cc", - ], - hdrs = [ - "socket_ipv6_udp_unbound.h", - ], - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - "@com_google_absl//absl/memory", - gtest, - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ipv4_udp_unbound_netlink_test_cases", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound_netlink.cc", - ], - hdrs = [ - "socket_ipv4_udp_unbound_netlink.h", - ], - deps = [ - ":socket_netlink_route_util", - ":socket_test_util", - "//test/util:capability_util", - "//test/util:cleanup", - gtest, - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ipv6_udp_unbound_netlink_test_cases", - testonly = 1, - srcs = [ - "socket_ipv6_udp_unbound_netlink.cc", - ], - hdrs = [ - "socket_ipv6_udp_unbound_netlink.h", - ], - deps = [ - ":socket_netlink_route_util", - ":socket_test_util", - "//test/util:capability_util", - gtest, - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ip_udp_unbound_external_networking", - testonly = 1, - srcs = [ - "socket_ip_udp_unbound_external_networking.cc", - ], - hdrs = [ - "socket_ip_udp_unbound_external_networking.h", - ], - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ipv4_udp_unbound_external_networking_test_cases", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound_external_networking.cc", - ], - hdrs = [ - "socket_ipv4_udp_unbound_external_networking.h", - ], - deps = [ - ":socket_ip_udp_unbound_external_networking", - gtest, - ], - alwayslink = 1, -) - -cc_library( - name = "socket_ipv6_udp_unbound_external_networking_test_cases", - testonly = 1, - srcs = [ - "socket_ipv6_udp_unbound_external_networking.cc", - ], - hdrs = [ - "socket_ipv6_udp_unbound_external_networking.h", - ], - deps = [ - ":socket_ip_udp_unbound_external_networking", - gtest, - ], - alwayslink = 1, -) - -cc_binary( - name = "socket_abstract_test", - testonly = 1, - srcs = [ - "socket_abstract.cc", - ], - linkstatic = 1, - deps = [ - ":socket_generic_test_cases", - ":socket_test_util", - ":socket_unix_cmsg_test_cases", - ":socket_unix_test_cases", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_abstract_non_blocking_test", - testonly = 1, - srcs = [ - "socket_unix_abstract_nonblock.cc", - ], - linkstatic = 1, - deps = [ - ":socket_non_blocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_dgram_local_test", - testonly = 1, - srcs = ["socket_unix_dgram_local.cc"], - linkstatic = 1, - deps = [ - ":socket_non_stream_test_cases", - ":socket_test_util", - ":socket_unix_dgram_test_cases", - ":socket_unix_non_stream_test_cases", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_dgram_non_blocking_test", - testonly = 1, - srcs = ["socket_unix_dgram_non_blocking.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_seqpacket_local_test", - testonly = 1, - srcs = [ - "socket_unix_seqpacket_local.cc", - ], - linkstatic = 1, - deps = [ - ":socket_non_stream_test_cases", - ":socket_test_util", - ":socket_unix_non_stream_test_cases", - ":socket_unix_seqpacket_test_cases", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_stream_test", - testonly = 1, - srcs = ["socket_unix_stream.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_tcp_generic_loopback_test", - testonly = 1, - srcs = [ - "socket_ip_tcp_generic_loopback.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ip_tcp_generic_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_tcp_udp_generic_loopback_test", - testonly = 1, - srcs = [ - "socket_ip_tcp_udp_generic.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_tcp_loopback_test", - testonly = 1, - srcs = [ - "socket_ip_tcp_loopback.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_generic_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_tcp_loopback_non_blocking_test", - testonly = 1, - srcs = [ - "socket_ip_tcp_loopback_nonblock.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_non_blocking_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_udp_loopback_test", - testonly = 1, - srcs = [ - "socket_ip_udp_loopback.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_generic_test_cases", - ":socket_ip_udp_test_cases", - ":socket_non_stream_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ipv4_udp_unbound_external_networking_test", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound_external_networking_test.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ipv4_udp_unbound_external_networking_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ipv6_udp_unbound_external_networking_test", - testonly = 1, - srcs = [ - "socket_ipv6_udp_unbound_external_networking_test.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ipv6_udp_unbound_external_networking_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_bind_to_device_test", - testonly = 1, - srcs = [ - "socket_bind_to_device.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_bind_to_device_util", - ":socket_test_util", - "//test/util:capability_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "socket_bind_to_device_sequence_test", - testonly = 1, - srcs = [ - "socket_bind_to_device_sequence.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_bind_to_device_util", - ":socket_test_util", - "//test/util:capability_util", - "@com_google_absl//absl/container:node_hash_map", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "socket_bind_to_device_distribution_test", - testonly = 1, - srcs = [ - "socket_bind_to_device_distribution.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_bind_to_device_util", - ":socket_test_util", - "//test/util:capability_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "socket_ip_udp_loopback_non_blocking_test", - testonly = 1, - srcs = [ - "socket_ip_udp_loopback_nonblock.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_non_blocking_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ipv4_udp_unbound_loopback_test", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound_loopback.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ipv4_udp_unbound_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ipv6_udp_unbound_loopback_test", - testonly = 1, - srcs = [ - "socket_ipv6_udp_unbound_loopback.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ipv6_udp_unbound_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ipv4_udp_unbound_loopback_nogotsan_test", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound_loopback_nogotsan.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/memory", - ], -) - -cc_binary( - name = "socket_ipv4_udp_unbound_loopback_netlink_test", - testonly = 1, - srcs = [ - "socket_ipv4_udp_unbound_loopback_netlink.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ipv4_udp_unbound_netlink_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ipv6_udp_unbound_loopback_netlink_test", - testonly = 1, - srcs = [ - "socket_ipv6_udp_unbound_loopback_netlink.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_ipv6_udp_unbound_netlink_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_unbound_test", - testonly = 1, - srcs = [ - "socket_ip_unbound.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_ip_unbound_netlink_test", - testonly = 1, - srcs = [ - "socket_ip_unbound_netlink.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_netlink_route_util", - ":socket_test_util", - "//test/util:capability_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_domain_test", - testonly = 1, - srcs = [ - "socket_unix_domain.cc", - ], - linkstatic = 1, - deps = [ - ":socket_generic_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_domain_non_blocking_test", - testonly = 1, - srcs = [ - "socket_unix_pair_nonblock.cc", - ], - linkstatic = 1, - deps = [ - ":socket_non_blocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_filesystem_test", - testonly = 1, - srcs = [ - "socket_filesystem.cc", - ], - linkstatic = 1, - deps = [ - ":socket_generic_test_cases", - ":socket_test_util", - ":socket_unix_cmsg_test_cases", - ":socket_unix_test_cases", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_filesystem_non_blocking_test", - testonly = 1, - srcs = [ - "socket_unix_filesystem_nonblock.cc", - ], - linkstatic = 1, - deps = [ - ":socket_non_blocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_inet_loopback_test", - testonly = 1, - srcs = ["socket_inet_loopback.cc"], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "socket_inet_loopback_nogotsan_test", - testonly = 1, - srcs = ["socket_inet_loopback_nogotsan.cc"], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "socket_netlink_test", - testonly = 1, - srcs = ["socket_netlink.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_netlink_route_test", - testonly = 1, - srcs = ["socket_netlink_route.cc"], - linkstatic = 1, - deps = [ - ":socket_netlink_route_util", - ":socket_netlink_util", - ":socket_test_util", - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings:str_format", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_netlink_uevent_test", - testonly = 1, - srcs = ["socket_netlink_uevent.cc"], - linkstatic = 1, - deps = [ - ":socket_netlink_util", - ":socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -# These socket tests are in a library because the test cases are shared -# across several test build targets. -cc_library( - name = "socket_stream_test_cases", - testonly = 1, - srcs = [ - "socket_stream.cc", - ], - hdrs = [ - "socket_stream.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_blocking_test_cases", - testonly = 1, - srcs = [ - "socket_blocking.cc", - ], - hdrs = [ - "socket_blocking.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_unix_test_cases", - testonly = 1, - srcs = [ - "socket_unix.cc", - ], - hdrs = [ - "socket_unix.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_util", - "//test/util:thread_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_unix_cmsg_test_cases", - testonly = 1, - srcs = [ - "socket_unix_cmsg.cc", - ], - hdrs = [ - "socket_unix_cmsg.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_util", - "//test/util:thread_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_stream_blocking_test_cases", - testonly = 1, - srcs = [ - "socket_stream_blocking.cc", - ], - hdrs = [ - "socket_stream_blocking.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_stream_nonblocking_test_cases", - testonly = 1, - srcs = [ - "socket_stream_nonblock.cc", - ], - hdrs = [ - "socket_stream_nonblock.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_non_stream_blocking_test_cases", - testonly = 1, - srcs = [ - "socket_non_stream_blocking.cc", - ], - hdrs = [ - "socket_non_stream_blocking.h", - ], - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_util", - "//test/util:thread_util", - ], - alwayslink = 1, -) - -cc_library( - name = "socket_bind_to_device_util", - testonly = 1, - srcs = [ - "socket_bind_to_device_util.cc", - ], - hdrs = [ - "socket_bind_to_device_util.h", - ], - deps = [ - "//test/util:test_util", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/strings", - ], - alwayslink = 1, -) - -cc_binary( - name = "socket_stream_local_test", - testonly = 1, - srcs = [ - "socket_unix_stream_local.cc", - ], - linkstatic = 1, - deps = [ - ":socket_stream_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_stream_blocking_local_test", - testonly = 1, - srcs = [ - "socket_unix_stream_blocking_local.cc", - ], - linkstatic = 1, - deps = [ - ":socket_stream_blocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_stream_blocking_tcp_test", - testonly = 1, - srcs = [ - "socket_ip_tcp_loopback_blocking.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_stream_blocking_test_cases", - ":socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_stream_nonblock_local_test", - testonly = 1, - srcs = [ - "socket_unix_stream_nonblock_local.cc", - ], - linkstatic = 1, - deps = [ - ":socket_stream_nonblocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_unbound_dgram_test", - testonly = 1, - srcs = ["socket_unix_unbound_dgram.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_unbound_abstract_test", - testonly = 1, - srcs = ["socket_unix_unbound_abstract.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_unbound_filesystem_test", - testonly = 1, - srcs = ["socket_unix_unbound_filesystem.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:file_descriptor", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_blocking_local_test", - testonly = 1, - srcs = [ - "socket_unix_blocking_local.cc", - ], - linkstatic = 1, - deps = [ - ":socket_blocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_blocking_ip_test", - testonly = 1, - srcs = [ - "socket_ip_loopback_blocking.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_blocking_test_cases", - ":socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_non_stream_blocking_local_test", - testonly = 1, - srcs = [ - "socket_unix_non_stream_blocking_local.cc", - ], - linkstatic = 1, - deps = [ - ":socket_non_stream_blocking_test_cases", - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_non_stream_blocking_udp_test", - testonly = 1, - srcs = [ - "socket_ip_udp_loopback_blocking.cc", - ], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_non_stream_blocking_test_cases", - ":socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_pair_test", - testonly = 1, - srcs = [ - "socket_unix_pair.cc", - ], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":socket_unix_cmsg_test_cases", - ":socket_unix_test_cases", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_unbound_seqpacket_test", - testonly = 1, - srcs = ["socket_unix_unbound_seqpacket.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_unix_unbound_stream_test", - testonly = 1, - srcs = ["socket_unix_unbound_stream.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - ":unix_domain_socket_test_util", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "socket_netdevice_test", - testonly = 1, - srcs = ["socket_netdevice.cc"], - linkstatic = 1, - deps = [ - ":socket_netlink_util", - ":socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/base:endian", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "stat_test", - testonly = 1, - srcs = [ - "file_base.h", - "stat.cc", - ], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:save_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "stat_times_test", - testonly = 1, - srcs = ["stat_times.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "statfs_test", - testonly = 1, - srcs = [ - "file_base.h", - "statfs.cc", - ], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "symlink_test", - testonly = 1, - srcs = ["symlink.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/time", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sync_test", - testonly = 1, - srcs = ["sync.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sysinfo_test", - testonly = 1, - srcs = ["sysinfo.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "syslog_test", - testonly = 1, - srcs = ["syslog.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sysret_test", - testonly = 1, - srcs = ["sysret.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "tcp_socket_test", - testonly = 1, - srcs = ["tcp_socket.cc"], - defines = select_system(), - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/time", - gtest, - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "tgkill_test", - testonly = 1, - srcs = ["tgkill.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "time_test", - testonly = 1, - srcs = ["time.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:proc_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "timerfd_test", - testonly = 1, - srcs = ["timerfd.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "timers_test", - testonly = 1, - srcs = ["timers.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:timer_util", - ], -) - -cc_binary( - name = "tkill_test", - testonly = 1, - srcs = ["tkill.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "truncate_test", - testonly = 1, - srcs = ["truncate.cc"], - linkstatic = 1, - deps = [ - ":file_base", - "//test/util:capability_util", - "//test/util:cleanup", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "tuntap_test", - testonly = 1, - srcs = ["tuntap.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - gtest, - ":socket_netlink_route_util", - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "tuntap_hostinet_test", - testonly = 1, - srcs = ["tuntap_hostinet.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "udp_socket_test", - testonly = 1, - srcs = ["udp_socket.cc"], - defines = select_system(), - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - ":socket_test_util", - ":unix_domain_socket_test_util", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/strings:str_format", - "@com_google_absl//absl/time", - gtest, - "//test/util:file_descriptor", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "udp_bind_test", - testonly = 1, - srcs = ["udp_bind.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - "//test/util:file_descriptor", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "uidgid_test", - testonly = 1, - srcs = ["uidgid.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/strings", - gtest, - "//test/util:cleanup", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:uid_util", - ], -) - -cc_binary( - name = "uname_test", - testonly = 1, - srcs = ["uname.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "unlink_test", - testonly = 1, - srcs = ["unlink.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "unshare_test", - testonly = 1, - srcs = ["unshare.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/synchronization", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "utimes_test", - testonly = 1, - srcs = ["utimes.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:fs_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "vdso_test", - testonly = 1, - srcs = ["vdso.cc"], - linkstatic = 1, - deps = [ - "//test/util:fs_util", - gtest, - "//test/util:posix_error", - "//test/util:proc_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "vfork_test", - testonly = 1, - srcs = ["vfork.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/flags:flag", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:test_util", - "//test/util:time_util", - ], -) - -cc_binary( - name = "wait_test", - testonly = 1, - srcs = ["wait.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:logging", - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:signal_util", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "//test/util:time_util", - ], -) - -cc_binary( - name = "write_test", - testonly = 1, - srcs = ["write.cc"], - linkstatic = 1, - deps = [ - "//test/util:cleanup", - "@com_google_absl//absl/base:core_headers", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "memory_accounting_test", - testonly = 1, - srcs = ["memory_accounting.cc"], - linkstatic = 1, - deps = [ - "//test/util:fs_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - gtest, - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "network_namespace_test", - testonly = 1, - srcs = ["network_namespace.cc"], - linkstatic = 1, - deps = [ - ":socket_test_util", - gtest, - "//test/util:capability_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "semaphore_test", - testonly = 1, - srcs = ["semaphore.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "@com_google_absl//absl/base:core_headers", - "@com_google_absl//absl/memory", - "@com_google_absl//absl/synchronization", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "shm_test", - testonly = 1, - srcs = ["shm.cc"], - linkstatic = 1, - deps = [ - "//test/util:multiprocess_util", - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "fadvise64_test", - testonly = 1, - srcs = ["fadvise64.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - gtest, - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "vdso_clock_gettime_test", - testonly = 1, - srcs = ["vdso_clock_gettime.cc"], - linkstatic = 1, - deps = [ - "@com_google_absl//absl/strings", - "@com_google_absl//absl/time", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "vsyscall_test", - testonly = 1, - srcs = ["vsyscall.cc"], - linkstatic = 1, - deps = [ - gtest, - "//test/util:proc_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "proc_net_unix_test", - testonly = 1, - srcs = ["proc_net_unix.cc"], - linkstatic = 1, - deps = [ - ":unix_domain_socket_test_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/strings:str_format", - gtest, - "//test/util:cleanup", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "memfd_test", - testonly = 1, - srcs = ["memfd.cc"], - linkstatic = 1, - deps = [ - "//test/util:file_descriptor", - "//test/util:fs_util", - gtest, - "//test/util:memory_util", - "//test/util:multiprocess_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "proc_net_tcp_test", - testonly = 1, - srcs = ["proc_net_tcp.cc"], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "proc_net_udp_test", - testonly = 1, - srcs = ["proc_net_udp.cc"], - linkstatic = 1, - deps = [ - ":ip_socket_test_util", - "//test/util:file_descriptor", - "@com_google_absl//absl/strings", - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "processes_test", - testonly = 1, - srcs = ["processes.cc"], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "xattr_test", - testonly = 1, - srcs = [ - "file_base.h", - "xattr.cc", - ], - linkstatic = 1, - deps = [ - "//test/util:capability_util", - "//test/util:file_descriptor", - "//test/util:fs_util", - "@com_google_absl//absl/container:flat_hash_set", - "@com_google_absl//absl/strings", - gtest, - "//test/util:posix_error", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) diff --git a/test/syscalls/linux/accept_bind.cc b/test/syscalls/linux/accept_bind.cc deleted file mode 100644 index f65a14fb8..000000000 --- a/test/syscalls/linux/accept_bind.cc +++ /dev/null @@ -1,641 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include <algorithm> -#include <vector> - -#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, Listen) { - 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(), /* backlog = */ 5), - SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, ListenIncreaseBacklog) { - 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(), /* backlog = */ 5), - SyscallSucceeds()); - ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 10), - SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, ListenDecreaseBacklog) { - 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(), /* backlog = */ 5), - SyscallSucceeds()); - ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 1), - SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, ListenWithoutBind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(listen(sockets->first_fd(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, DoubleBind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->second_addr(), - sockets->second_addr_size()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, BindListenBind) { - 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(bind(sockets->first_fd(), sockets->second_addr(), - sockets->second_addr_size()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, DoubleListen) { - 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(listen(sockets->first_fd(), 5), SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, DoubleConnect) { - 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()); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(EISCONN)); -} - -TEST_P(AllSocketPairTest, Connect) { - 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()); -} - -TEST_P(AllSocketPairTest, ConnectWithWrongType) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int type; - socklen_t typelen = sizeof(type); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_TYPE, &type, &typelen), - SyscallSucceeds()); - switch (type) { - case SOCK_STREAM: - type = SOCK_SEQPACKET; - break; - case SOCK_SEQPACKET: - type = SOCK_STREAM; - break; - } - - const FileDescriptor another_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, type, 0)); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); - - if (sockets->first_addr()->sa_data[0] != 0) { - ASSERT_THAT(connect(another_socket.get(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(EPROTOTYPE)); - } else { - ASSERT_THAT(connect(another_socket.get(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(ECONNREFUSED)); - } - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, ConnectNonListening) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -TEST_P(AllSocketPairTest, ConnectToFilePath) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = {}; - addr.sun_family = AF_UNIX; - constexpr char kPath[] = "/tmp"; - memcpy(addr.sun_path, kPath, sizeof(kPath)); - - ASSERT_THAT( - connect(sockets->second_fd(), - reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -TEST_P(AllSocketPairTest, ConnectToInvalidAbstractPath) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = {}; - addr.sun_family = AF_UNIX; - constexpr char kPath[] = "\0nonexistent"; - memcpy(addr.sun_path, kPath, sizeof(kPath)); - - ASSERT_THAT( - connect(sockets->second_fd(), - reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -TEST_P(AllSocketPairTest, SelfConnect) { - 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->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, ConnectWithoutListen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -TEST_P(AllSocketPairTest, Accept) { - 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()); - ASSERT_THAT(close(accepted), SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, AcceptValidAddrLen) { - 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; - struct sockaddr_un addr = {}; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT( - accepted = accept(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(&addr), &addr_len), - SyscallSucceeds()); - ASSERT_THAT(close(accepted), SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, AcceptNegativeAddrLen) { - 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()); - - // With a negative addr_len, accept returns EINVAL, - struct sockaddr_un addr = {}; - socklen_t addr_len = -1; - ASSERT_THAT(accept(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(&addr), &addr_len), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, AcceptLargePositiveAddrLen) { - 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()); - - // With a large (positive) addr_len, accept does not return EINVAL. - int accepted = -1; - char addr_buf[200]; - socklen_t addr_len = sizeof(addr_buf); - ASSERT_THAT(accepted = accept(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(addr_buf), - &addr_len), - SyscallSucceeds()); - // addr_len should have been updated by accept(). - EXPECT_LT(addr_len, sizeof(addr_buf)); - ASSERT_THAT(close(accepted), SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, AcceptVeryLargePositiveAddrLen) { - 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()); - - // With a large (positive) addr_len, accept does not return EINVAL. - int accepted = -1; - char addr_buf[2000]; - socklen_t addr_len = sizeof(addr_buf); - ASSERT_THAT(accepted = accept(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(addr_buf), - &addr_len), - SyscallSucceeds()); - // addr_len should have been updated by accept(). - EXPECT_LT(addr_len, sizeof(addr_buf)); - ASSERT_THAT(close(accepted), SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, AcceptWithoutBind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(accept(sockets->first_fd(), nullptr, nullptr), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, AcceptWithoutListen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(accept(sockets->first_fd(), nullptr, nullptr), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, GetRemoteAddress) { - 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()); - - socklen_t addr_len = sockets->first_addr_size(); - struct sockaddr_storage addr = {}; - ASSERT_THAT( - getpeername(sockets->second_fd(), (struct sockaddr*)(&addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, sockets->first_addr_len()); - EXPECT_EQ(0, memcmp(&addr, sockets->first_addr(), sockets->first_addr_len())); -} - -TEST_P(AllSocketPairTest, UnboundGetLocalAddress) { - 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()); - - socklen_t addr_len = sockets->first_addr_size(); - struct sockaddr_storage addr = {}; - ASSERT_THAT( - getsockname(sockets->second_fd(), (struct sockaddr*)(&addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, 2); - EXPECT_EQ( - memcmp(&addr, sockets->second_addr(), - std::min((size_t)addr_len, (size_t)sockets->second_addr_len())), - 0); -} - -TEST_P(AllSocketPairTest, BoundGetLocalAddress) { - 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(bind(sockets->second_fd(), sockets->second_addr(), - sockets->second_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - socklen_t addr_len = sockets->first_addr_size(); - struct sockaddr_storage addr = {}; - ASSERT_THAT( - getsockname(sockets->second_fd(), (struct sockaddr*)(&addr), &addr_len), - SyscallSucceeds()); - 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); -} - -TEST_P(AllSocketPairTest, BoundConnector) { - 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(bind(sockets->second_fd(), sockets->second_addr(), - sockets->second_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, UnboundSenderAddr) { - 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 accepted_fd(accepted); - - int i = 0; - ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT( - RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&addr), &addr_len), - SyscallSucceedsWithValue(sizeof(i))); - EXPECT_EQ(addr_len, 0); -} - -TEST_P(AllSocketPairTest, BoundSenderAddr) { - 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(bind(sockets->second_fd(), sockets->second_addr(), - sockets->second_addr_size()), - 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 accepted_fd(accepted); - - int i = 0; - ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT( - RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&addr), &addr_len), - SyscallSucceedsWithValue(sizeof(i))); - 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); -} - -TEST_P(AllSocketPairTest, BindAfterConnectSenderAddr) { - 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()); - - ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), - sockets->second_addr_size()), - SyscallSucceeds()); - - int accepted = -1; - ASSERT_THAT(accepted = accept(sockets->first_fd(), nullptr, nullptr), - SyscallSucceeds()); - FileDescriptor accepted_fd(accepted); - - int i = 0; - ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT( - RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&addr), &addr_len), - SyscallSucceedsWithValue(sizeof(i))); - 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); -} - -TEST_P(AllSocketPairTest, BindAfterAcceptSenderAddr) { - 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 accepted_fd(accepted); - - ASSERT_THAT(bind(sockets->second_fd(), sockets->second_addr(), - sockets->second_addr_size()), - SyscallSucceeds()); - - int i = 0; - ASSERT_THAT(RetryEINTR(send)(sockets->second_fd(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - - struct sockaddr_storage addr; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT( - RetryEINTR(recvfrom)(accepted_fd.get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&addr), &addr_len), - SyscallSucceedsWithValue(sizeof(i))); - 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_SUITE_P( - AllUnixDomainSockets, AllSocketPairTest, - ::testing::ValuesIn(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK}))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/accept_bind_stream.cc b/test/syscalls/linux/accept_bind_stream.cc deleted file mode 100644 index 4857f160b..000000000 --- a/test/syscalls/linux/accept_bind_stream.cc +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/un.h> - -#include <algorithm> -#include <vector> - -#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_SUITE_P( - AllUnixDomainSockets, AllSocketPairTest, - ::testing::ValuesIn(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>(FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK}))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/access.cc b/test/syscalls/linux/access.cc deleted file mode 100644 index bcc25cef4..000000000 --- a/test/syscalls/linux/access.cc +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <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 diff --git a/test/syscalls/linux/affinity.cc b/test/syscalls/linux/affinity.cc deleted file mode 100644 index 128364c34..000000000 --- a/test/syscalls/linux/affinity.cc +++ /dev/null @@ -1,242 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sched.h> -#include <sys/syscall.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()) { - GTEST_SKIP() << "Skipping test: cpu " << cpu << " exists"; - } - 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) { - SKIP_IF(CPU_COUNT(&mask_) < 3); - 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 diff --git a/test/syscalls/linux/aio.cc b/test/syscalls/linux/aio.cc deleted file mode 100644 index 806d5729e..000000000 --- a/test/syscalls/linux/aio.cc +++ /dev/null @@ -1,430 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <linux/aio_abi.h> -#include <sys/mman.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <string> - -#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/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/proc_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::_; - -namespace gvisor { -namespace testing { -namespace { - -// Returns the size of the VMA containing the given address. -PosixErrorOr<size_t> VmaSizeAt(uintptr_t addr) { - ASSIGN_OR_RETURN_ERRNO(std::string proc_self_maps, - GetContents("/proc/self/maps")); - ASSIGN_OR_RETURN_ERRNO(auto entries, ParseProcMaps(proc_self_maps)); - // Use binary search to find the first VMA that might contain addr. - ProcMapsEntry target = {}; - target.end = addr; - auto it = - std::upper_bound(entries.begin(), entries.end(), target, - [](const ProcMapsEntry& x, const ProcMapsEntry& y) { - return x.end < y.end; - }); - // Check that it actually contains addr. - if (it == entries.end() || addr < it->start) { - return PosixError(ENOENT, absl::StrCat("no VMA contains address ", addr)); - } - return it->end - it->start; -} - -constexpr char kData[] = "hello world!"; - -int SubmitCtx(aio_context_t ctx, long nr, struct iocb** iocbpp) { - return syscall(__NR_io_submit, ctx, nr, iocbpp); -} - -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()); - ctx_ = 0; - } - } - - 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(gvisor.dev/issue/204): 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[sizeof(kData)] = {}; - ASSERT_THAT(read(test_file_fd_.get(), verify_buf, sizeof(kData)), - SyscallSucceedsWithValue(strlen(kData))); - EXPECT_STREQ(verify_buf, kData); -} - -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 100 entries deep. - ASSERT_THAT(SetupContext(100), SyscallSucceeds()); - - struct iocb cb = CreateCallback(); - struct iocb* cbs[] = {&cb}; - - // Submit a request but don't complete it to make it pending. - for (int i = 0; i < 100; ++i) { - EXPECT_THAT(Submit(1, cbs), SyscallSucceeds()); - } - - ASSERT_THAT(DestroyContext(), SyscallSucceeds()); - ctx_ = 0; -} - -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()); - const size_t ctx_size = - ASSERT_NO_ERRNO_AND_VALUE(VmaSizeAt(reinterpret_cast<uintptr_t>(ctx_))); - - struct iocb cb = CreateCallback(); - struct iocb* cbs[1] = {&cb}; - - // Reserve address space for the mremap target so we have something safe to - // map over. - Mapping dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(ctx_size, PROT_READ, MAP_PRIVATE)); - - // Remap context 'handle' to a different address. - ASSERT_THAT(Mremap(reinterpret_cast<void*>(ctx_), ctx_size, dst.len(), - MREMAP_FIXED | MREMAP_MAYMOVE, dst.ptr()), - IsPosixErrorOkAndHolds(dst.ptr())); - aio_context_t old_ctx = ctx_; - ctx_ = reinterpret_cast<aio_context_t>(dst.addr()); - // io_destroy() will unmap dst now. - dst.release(); - - // 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. - dst = ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(ctx_size, PROT_READ, MAP_PRIVATE)); - ASSERT_THAT(Mremap(reinterpret_cast<void*>(ctx_), ctx_size, dst.len(), - MREMAP_FIXED | MREMAP_MAYMOVE, dst.ptr()), - IsPosixErrorOkAndHolds(dst.ptr())); - ctx_ = reinterpret_cast<aio_context_t>(dst.addr()); - dst.release(); - - // 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[sizeof(kData)] = {}; - ASSERT_THAT(read(test_file_fd_.get(), verify_buf, sizeof(kData)), - SyscallSucceedsWithValue(strlen(kData))); - EXPECT_STREQ(verify_buf, kData); -} - -// Tests that AIO context cannot be expanded with mremap. -TEST_F(AIOTest, MremapExpansion) { - // Setup a context that is 128 entries deep. - ASSERT_THAT(SetupContext(128), SyscallSucceeds()); - const size_t ctx_size = - ASSERT_NO_ERRNO_AND_VALUE(VmaSizeAt(reinterpret_cast<uintptr_t>(ctx_))); - - // Reserve address space for the mremap target so we have something safe to - // map over. - Mapping dst = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(ctx_size + kPageSize, PROT_NONE, MAP_PRIVATE)); - - // Test that remapping to a larger address range fails. - ASSERT_THAT(Mremap(reinterpret_cast<void*>(ctx_), ctx_size, dst.len(), - MREMAP_FIXED | MREMAP_MAYMOVE, dst.ptr()), - PosixErrorIs(EFAULT, _)); - - // mm/mremap.c:sys_mremap() => mremap_to() does do_munmap() of the destination - // before it hits the VM_DONTEXPAND check in vma_to_resize(), so we should no - // longer munmap it (another thread may have created a mapping there). - dst.release(); -} - -// 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_SUITE_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_SUITE_P(BadIOVecs, AIOVectorizedParamTest, - ::testing::Values(IOCB_CMD_PREADV, IOCB_CMD_PWRITEV)); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/alarm.cc b/test/syscalls/linux/alarm.cc deleted file mode 100644 index 940c97285..000000000 --- a/test/syscalls/linux/alarm.cc +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <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 gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/arch_prctl.cc b/test/syscalls/linux/arch_prctl.cc deleted file mode 100644 index 81bf5a775..000000000 --- a/test/syscalls/linux/arch_prctl.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <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 diff --git a/test/syscalls/linux/bad.cc b/test/syscalls/linux/bad.cc deleted file mode 100644 index a26fc6af3..000000000 --- a/test/syscalls/linux/bad.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/syscall.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { -#ifdef __x86_64__ -// get_kernel_syms is not supported in Linux > 2.6, and not implemented in -// gVisor. -constexpr uint32_t kNotImplementedSyscall = SYS_get_kernel_syms; -#elif __aarch64__ -// Use the last of arch_specific_syscalls which are not implemented on arm64. -constexpr uint32_t kNotImplementedSyscall = __NR_arch_specific_syscall + 15; -#endif - -TEST(BadSyscallTest, NotImplemented) { - EXPECT_THAT(syscall(kNotImplementedSyscall), SyscallFailsWithErrno(ENOSYS)); -} - -TEST(BadSyscallTest, NegativeOne) { - EXPECT_THAT(syscall(-1), SyscallFailsWithErrno(ENOSYS)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/base_poll_test.cc b/test/syscalls/linux/base_poll_test.cc deleted file mode 100644 index ab7a19dd0..000000000 --- a/test/syscalls/linux/base_poll_test.cc +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/base_poll_test.h" - -#include <sys/syscall.h> -#include <sys/types.h> -#include <syscall.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -static volatile int timer_fired = 0; -static void SigAlarmHandler(int, siginfo_t*, void*) { timer_fired = 1; } - -BasePollTest::BasePollTest() { - // Register our SIGALRM handler, but save the original so we can restore in - // the destructor. - struct sigaction sa = {}; - sa.sa_sigaction = SigAlarmHandler; - sigfillset(&sa.sa_mask); - TEST_PCHECK(sigaction(SIGALRM, &sa, &original_alarm_sa_) == 0); -} - -BasePollTest::~BasePollTest() { - ClearTimer(); - TEST_PCHECK(sigaction(SIGALRM, &original_alarm_sa_, nullptr) == 0); -} - -void BasePollTest::SetTimer(absl::Duration duration) { - pid_t tgid = getpid(); - pid_t tid = gettid(); - ClearTimer(); - - // Create a new timer thread. - timer_ = absl::make_unique<TimerThread>(absl::Now() + duration, tgid, tid); -} - -bool BasePollTest::TimerFired() const { return timer_fired; } - -void BasePollTest::ClearTimer() { - timer_.reset(); - timer_fired = 0; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/base_poll_test.h b/test/syscalls/linux/base_poll_test.h deleted file mode 100644 index 0d4a6701e..000000000 --- a/test/syscalls/linux/base_poll_test.h +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_BASE_POLL_TEST_H_ -#define GVISOR_TEST_SYSCALLS_BASE_POLL_TEST_H_ - -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <syscall.h> -#include <time.h> -#include <unistd.h> - -#include <memory> - -#include "gtest/gtest.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/time.h" -#include "test/util/logging.h" -#include "test/util/signal_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -// TimerThread is a cancelable timer. -class TimerThread { - public: - TimerThread(absl::Time deadline, pid_t tgid, pid_t tid) - : thread_([=] { - mu_.Lock(); - mu_.AwaitWithDeadline(absl::Condition(&cancel_), deadline); - if (!cancel_) { - TEST_PCHECK(tgkill(tgid, tid, SIGALRM) == 0); - } - mu_.Unlock(); - }) {} - - ~TimerThread() { Cancel(); } - - void Cancel() { - absl::MutexLock ml(&mu_); - cancel_ = true; - } - - private: - mutable absl::Mutex mu_; - bool cancel_ ABSL_GUARDED_BY(mu_) = false; - - // Must be last to ensure that the destructor for the thread is run before - // any other member of the object is destroyed. - ScopedThread thread_; -}; - -// Base test fixture for poll, select, ppoll, and pselect tests. -// -// This fixture makes use of SIGALRM. The handler is saved in SetUp() and -// restored in TearDown(). -class BasePollTest : public ::testing::Test { - protected: - BasePollTest(); - ~BasePollTest() override; - - // Sets a timer that will send a signal to the calling thread after - // `duration`. - void SetTimer(absl::Duration duration); - - // Returns true if the timer has fired. - bool TimerFired() const; - - // Stops the pending timer (if any) and clear the "fired" state. - void ClearTimer(); - - private: - // Thread that implements the timer. If the timer is stopped, timer_ is null. - // - // We have to use a thread for this purpose because tests using this fixture - // expect to be interrupted by the timer signal, but itimers/alarm(2) send - // thread-group-directed signals, which may be handled by any thread in the - // test process. - std::unique_ptr<TimerThread> timer_; - - // The original SIGALRM handler, to restore in destructor. - struct sigaction original_alarm_sa_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_BASE_POLL_TEST_H_ diff --git a/test/syscalls/linux/bind.cc b/test/syscalls/linux/bind.cc deleted file mode 100644 index 9547c4ab2..000000000 --- a/test/syscalls/linux/bind.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST_P(AllSocketPairTest, Bind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(AllSocketPairTest, BindTooLong) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - // first_addr is a sockaddr_storage being used as a sockaddr_un. Use the full - // length which is longer than expected for a Unix socket. - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sizeof(sockaddr_storage)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, DoubleBindSocket) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - EXPECT_THAT( - bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - // Linux 4.09 returns EINVAL here, but some time before 4.19 it switched - // to EADDRINUSE. - AnyOf(SyscallFailsWithErrno(EADDRINUSE), SyscallFailsWithErrno(EINVAL))); -} - -TEST_P(AllSocketPairTest, GetLocalAddr) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - socklen_t addressLength = sockets->first_addr_size(); - struct sockaddr_storage address = {}; - ASSERT_THAT(getsockname(sockets->first_fd(), (struct sockaddr*)(&address), - &addressLength), - SyscallSucceeds()); - EXPECT_EQ( - 0, memcmp(&address, sockets->first_addr(), sockets->first_addr_size())); -} - -TEST_P(AllSocketPairTest, GetLocalAddrWithoutBind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - socklen_t addressLength = sockets->first_addr_size(); - struct sockaddr_storage received_address = {}; - ASSERT_THAT( - getsockname(sockets->first_fd(), (struct sockaddr*)(&received_address), - &addressLength), - SyscallSucceeds()); - struct sockaddr_storage want_address = {}; - want_address.ss_family = sockets->first_addr()->sa_family; - EXPECT_EQ(0, memcmp(&received_address, &want_address, addressLength)); -} - -TEST_P(AllSocketPairTest, GetRemoteAddressWithoutConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - socklen_t addressLength = sockets->first_addr_size(); - struct sockaddr_storage address = {}; - ASSERT_THAT(getpeername(sockets->second_fd(), (struct sockaddr*)(&address), - &addressLength), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(AllSocketPairTest, DoubleBindAddress) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - EXPECT_THAT(bind(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(AllSocketPairTest, Unbind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - - // Filesystem Unix sockets do not release their address when closed. - if (sockets->first_addr()->sa_data[0] != 0) { - ASSERT_THAT(bind(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallFailsWithErrno(EADDRINUSE)); - return; - } - - ASSERT_THAT(bind(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, AllSocketPairTest, - ::testing::ValuesIn(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, - SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, - SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK}))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/brk.cc b/test/syscalls/linux/brk.cc deleted file mode 100644 index a03a44465..000000000 --- a/test/syscalls/linux/brk.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdint.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -TEST(BrkTest, BrkSyscallReturnsOldBrkOnFailure) { - auto old_brk = sbrk(0); - EXPECT_THAT(syscall(SYS_brk, reinterpret_cast<void*>(-1)), - SyscallSucceedsWithValue(reinterpret_cast<uintptr_t>(old_brk))); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/chdir.cc b/test/syscalls/linux/chdir.cc deleted file mode 100644 index 3182c228b..000000000 --- a/test/syscalls/linux/chdir.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <linux/limits.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "test/util/capability_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ChdirTest, Success) { - auto old_dir = GetAbsoluteTestTmpdir(); - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(chdir(temp_dir.path().c_str()), SyscallSucceeds()); - // Temp path destructor deletes the newly created tmp dir and Sentry rejects - // saving when its current dir is still pointing to the path. Switch to a - // permanent path here. - EXPECT_THAT(chdir(old_dir.c_str()), SyscallSucceeds()); -} - -TEST(ChdirTest, PermissionDenied) { - // Drop capabilities that allow us to override directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); - EXPECT_THAT(chdir(temp_dir.path().c_str()), SyscallFailsWithErrno(EACCES)); -} - -TEST(ChdirTest, NotDir) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - EXPECT_THAT(chdir(temp_file.path().c_str()), SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(ChdirTest, NotExist) { - EXPECT_THAT(chdir("/foo/bar"), SyscallFailsWithErrno(ENOENT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/chmod.cc b/test/syscalls/linux/chmod.cc deleted file mode 100644 index 8233df0f8..000000000 --- a/test/syscalls/linux/chmod.cc +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ChmodTest, ChmodFileSucceeds) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - ASSERT_THAT(chmod(file.path().c_str(), 0466), SyscallSucceeds()); - EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, ChmodDirSucceeds) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string fileInDir = NewTempAbsPathInDir(dir.path()); - - ASSERT_THAT(chmod(dir.path().c_str(), 0466), SyscallSucceeds()); - EXPECT_THAT(open(fileInDir.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodFileSucceeds_NoRandomSave) { - // Drop capabilities that allow us to file directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); - int fd; - ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds()); - - { - const DisableSave ds; // File permissions are reduced. - ASSERT_THAT(fchmod(fd, 0444), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - } - - EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodDirSucceeds_NoRandomSave) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - int fd; - ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - - { - const DisableSave ds; // File permissions are reduced. - ASSERT_THAT(fchmod(fd, 0), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - } - - EXPECT_THAT(open(dir.path().c_str(), O_RDONLY), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodBadF) { - ASSERT_THAT(fchmod(-1, 0444), SyscallFailsWithErrno(EBADF)); -} - -TEST(ChmodTest, FchmodatBadF) { - ASSERT_THAT(fchmodat(-1, "foo", 0444, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(ChmodTest, FchmodFileWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - ASSERT_THAT(fchmod(fd.get(), 0444), SyscallFailsWithErrno(EBADF)); -} - -TEST(ChmodTest, FchmodDirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH)); - - ASSERT_THAT(fchmod(fd.get(), 0444), SyscallFailsWithErrno(EBADF)); -} - -TEST(ChmodTest, FchmodatWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - const auto parent_fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(GetAbsoluteTestTmpdir().c_str(), O_PATH | O_DIRECTORY)); - - ASSERT_THAT( - fchmodat(parent_fd.get(), std::string(Basename(temp_file.path())).c_str(), - 0444, 0), - SyscallSucceeds()); - - EXPECT_THAT(open(temp_file.path().c_str(), O_RDWR), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodatNotDir) { - ASSERT_THAT(fchmodat(-1, "", 0444, 0), SyscallFailsWithErrno(ENOENT)); -} - -TEST(ChmodTest, FchmodatFileAbsolutePath) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - ASSERT_THAT(fchmodat(-1, file.path().c_str(), 0444, 0), SyscallSucceeds()); - EXPECT_THAT(open(file.path().c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodatDirAbsolutePath) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - int fd; - ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - ASSERT_THAT(fchmodat(-1, dir.path().c_str(), 0, 0), SyscallSucceeds()); - EXPECT_THAT(open(dir.path().c_str(), O_RDONLY), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodatFile) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - int parent_fd; - ASSERT_THAT( - parent_fd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - - ASSERT_THAT( - fchmodat(parent_fd, std::string(Basename(temp_file.path())).c_str(), 0444, - 0), - SyscallSucceeds()); - EXPECT_THAT(close(parent_fd), SyscallSucceeds()); - - EXPECT_THAT(open(temp_file.path().c_str(), O_RDWR), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodatDir) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - int parent_fd; - ASSERT_THAT( - parent_fd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - - int fd; - ASSERT_THAT(fd = open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - ASSERT_THAT( - fchmodat(parent_fd, std::string(Basename(dir.path())).c_str(), 0, 0), - SyscallSucceeds()); - EXPECT_THAT(close(parent_fd), SyscallSucceeds()); - - EXPECT_THAT(open(dir.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, ChmodDowngradeWritability_NoRandomSave) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); - - int fd; - ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds()); - - const DisableSave ds; // Permissions are dropped. - ASSERT_THAT(chmod(file.path().c_str(), 0444), SyscallSucceeds()); - EXPECT_THAT(write(fd, "hello", 5), SyscallSucceedsWithValue(5)); - - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(ChmodTest, ChmodFileToNoPermissionsSucceeds) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); - - ASSERT_THAT(chmod(file.path().c_str(), 0), SyscallSucceeds()); - - EXPECT_THAT(open(file.path().c_str(), O_RDONLY), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ChmodTest, FchmodDowngradeWritability_NoRandomSave) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - int fd; - ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - - const DisableSave ds; // Permissions are dropped. - ASSERT_THAT(fchmod(fd, 0444), SyscallSucceeds()); - EXPECT_THAT(write(fd, "hello", 5), SyscallSucceedsWithValue(5)); - - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(ChmodTest, FchmodFileToNoPermissionsSucceeds_NoRandomSave) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); - - int fd; - ASSERT_THAT(fd = open(file.path().c_str(), O_RDWR), SyscallSucceeds()); - - { - const DisableSave ds; // Permissions are dropped. - ASSERT_THAT(fchmod(fd, 0), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - } - - EXPECT_THAT(open(file.path().c_str(), O_RDONLY), - SyscallFailsWithErrno(EACCES)); -} - -// Verify that we can get a RW FD after chmod, even if a RO fd is left open. -TEST(ChmodTest, ChmodWritableWithOpenFD) { - // FIXME(b/72455313): broken on hostfs. - if (IsRunningOnGvisor()) { - return; - } - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0444)); - - FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - ASSERT_THAT(fchmod(fd1.get(), 0644), SyscallSucceeds()); - - // This FD is writable, even though fd1 has a read-only reference to the file. - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - // fd1 is not writable, but fd2 is. - char c = 'a'; - EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/chown.cc b/test/syscalls/linux/chown.cc deleted file mode 100644 index ff0d39343..000000000 --- a/test/syscalls/linux/chown.cc +++ /dev/null @@ -1,255 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <grp.h> -#include <sys/types.h> -#include <unistd.h> - -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/synchronization/notification.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(int32_t, scratch_uid1, 65534, "first scratch UID"); -ABSL_FLAG(int32_t, scratch_uid2, 65533, "second scratch UID"); -ABSL_FLAG(int32_t, scratch_gid, 65534, "first scratch GID"); - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ChownTest, FchownBadF) { - ASSERT_THAT(fchown(-1, 0, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(ChownTest, FchownatBadF) { - ASSERT_THAT(fchownat(-1, "fff", 0, 0, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(ChownTest, FchownFileWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - ASSERT_THAT(fchown(fd.get(), geteuid(), getegid()), - SyscallFailsWithErrno(EBADF)); -} - -TEST(ChownTest, FchownDirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH)); - - ASSERT_THAT(fchown(fd.get(), geteuid(), getegid()), - SyscallFailsWithErrno(EBADF)); -} - -TEST(ChownTest, FchownatWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - const auto dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_PATH)); - ASSERT_THAT( - fchownat(dirfd.get(), file.path().c_str(), geteuid(), getegid(), 0), - SyscallSucceeds()); -} - -TEST(ChownTest, FchownatEmptyPath) { - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY | O_RDONLY)); - ASSERT_THAT(fchownat(fd.get(), "", 0, 0, 0), SyscallFailsWithErrno(ENOENT)); -} - -using Chown = - std::function<PosixError(const std::string&, uid_t owner, gid_t group)>; - -class ChownParamTest : public ::testing::TestWithParam<Chown> {}; - -TEST_P(ChownParamTest, ChownFileSucceeds) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_CHOWN))) { - ASSERT_NO_ERRNO(SetCapability(CAP_CHOWN, false)); - } - - const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // At least *try* setting to a group other than the EGID. - gid_t gid; - EXPECT_THAT(gid = getegid(), SyscallSucceeds()); - int num_groups; - EXPECT_THAT(num_groups = getgroups(0, nullptr), SyscallSucceeds()); - if (num_groups > 0) { - std::vector<gid_t> list(num_groups); - EXPECT_THAT(getgroups(list.size(), list.data()), SyscallSucceeds()); - // Scan the list of groups for a valid gid. Note that if a group is not - // defined in this local user namespace, then we will see 65534, and the - // group will not chown below as expected. So only change if we find a - // valid group in this list. - for (const gid_t other_gid : list) { - if (other_gid != 65534) { - gid = other_gid; - break; - } - } - } - - EXPECT_NO_ERRNO(GetParam()(file.path(), geteuid(), gid)); - - struct stat s = {}; - ASSERT_THAT(stat(file.path().c_str(), &s), SyscallSucceeds()); - EXPECT_EQ(s.st_uid, geteuid()); - EXPECT_EQ(s.st_gid, gid); -} - -TEST_P(ChownParamTest, ChownFilePermissionDenied) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0777)); - EXPECT_THAT(chmod(GetAbsoluteTestTmpdir().c_str(), 0777), SyscallSucceeds()); - - // Drop privileges and change IDs only in child thread, or else this parent - // thread won't be able to open some log files after the test ends. - ScopedThread([&] { - // Drop privileges. - if (HaveCapability(CAP_CHOWN).ValueOrDie()) { - EXPECT_NO_ERRNO(SetCapability(CAP_CHOWN, false)); - } - - // Change EUID and EGID. - // - // See note about POSIX below. - EXPECT_THAT( - syscall(SYS_setresgid, -1, absl::GetFlag(FLAGS_scratch_gid), -1), - SyscallSucceeds()); - EXPECT_THAT( - syscall(SYS_setresuid, -1, absl::GetFlag(FLAGS_scratch_uid1), -1), - SyscallSucceeds()); - - EXPECT_THAT(GetParam()(file.path(), geteuid(), getegid()), - PosixErrorIs(EPERM, ::testing::ContainsRegex("chown"))); - }); -} - -TEST_P(ChownParamTest, ChownFileSucceedsAsRoot) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_CHOWN)))); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_SETUID)))); - - const std::string filename = NewTempAbsPath(); - EXPECT_THAT(chmod(GetAbsoluteTestTmpdir().c_str(), 0777), SyscallSucceeds()); - - absl::Notification fileCreated, fileChowned; - // Change UID only in child thread, or else this parent thread won't be able - // to open some log files after the test ends. - ScopedThread t([&] { - // POSIX requires that all threads in a process share the same UIDs, so - // the NPTL setresuid wrappers use signals to make all threads execute the - // setresuid syscall. However, we want this thread to have its own set of - // credentials different from the parent process, so we use the raw - // syscall. - EXPECT_THAT( - syscall(SYS_setresuid, -1, absl::GetFlag(FLAGS_scratch_uid2), -1), - SyscallSucceeds()); - - // Create file and immediately close it. - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0644)); - fd.reset(); // Close the fd. - - fileCreated.Notify(); - fileChowned.WaitForNotification(); - - EXPECT_THAT(open(filename.c_str(), O_RDWR), SyscallFailsWithErrno(EACCES)); - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDONLY)); - }); - - fileCreated.WaitForNotification(); - - // Set file's owners to someone different. - EXPECT_NO_ERRNO(GetParam()(filename, absl::GetFlag(FLAGS_scratch_uid1), - absl::GetFlag(FLAGS_scratch_gid))); - - struct stat s; - EXPECT_THAT(stat(filename.c_str(), &s), SyscallSucceeds()); - EXPECT_EQ(s.st_uid, absl::GetFlag(FLAGS_scratch_uid1)); - EXPECT_EQ(s.st_gid, absl::GetFlag(FLAGS_scratch_gid)); - - fileChowned.Notify(); -} - -PosixError errorFromReturn(const std::string& name, int ret) { - if (ret == -1) { - return PosixError(errno, absl::StrCat(name, " failed")); - } - return NoError(); -} - -INSTANTIATE_TEST_SUITE_P( - ChownKinds, ChownParamTest, - ::testing::Values( - [](const std::string& path, uid_t owner, gid_t group) -> PosixError { - int rc = chown(path.c_str(), owner, group); - MaybeSave(); - return errorFromReturn("chown", rc); - }, - [](const std::string& path, uid_t owner, gid_t group) -> PosixError { - int rc = lchown(path.c_str(), owner, group); - MaybeSave(); - return errorFromReturn("lchown", rc); - }, - [](const std::string& path, uid_t owner, gid_t group) -> PosixError { - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, O_RDWR)); - int rc = fchown(fd.get(), owner, group); - MaybeSave(); - return errorFromReturn("fchown", rc); - }, - [](const std::string& path, uid_t owner, gid_t group) -> PosixError { - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, O_RDWR)); - int rc = fchownat(fd.get(), "", owner, group, AT_EMPTY_PATH); - MaybeSave(); - return errorFromReturn("fchownat-fd", rc); - }, - [](const std::string& path, uid_t owner, gid_t group) -> PosixError { - ASSIGN_OR_RETURN_ERRNO(auto dirfd, Open(std::string(Dirname(path)), - O_DIRECTORY | O_RDONLY)); - int rc = fchownat(dirfd.get(), std::string(Basename(path)).c_str(), - owner, group, 0); - MaybeSave(); - return errorFromReturn("fchownat-dirfd", rc); - }, - [](const std::string& path, uid_t owner, gid_t group) -> PosixError { - ASSIGN_OR_RETURN_ERRNO(auto dirfd, Open(std::string(Dirname(path)), - O_DIRECTORY | O_PATH)); - int rc = fchownat(dirfd.get(), std::string(Basename(path)).c_str(), - owner, group, 0); - MaybeSave(); - return errorFromReturn("fchownat-opathdirfd", rc); - })); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/chroot.cc b/test/syscalls/linux/chroot.cc deleted file mode 100644 index fab79d300..000000000 --- a/test/syscalls/linux/chroot.cc +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <stddef.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <syscall.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/logging.h" -#include "test/util/mount_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::HasSubstr; -using ::testing::Not; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ChrootTest, Success) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - const auto rest = [] { - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str())); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST(ChrootTest, PermissionDenied) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - // CAP_DAC_READ_SEARCH and CAP_DAC_OVERRIDE may override Execute permission - // on directories. - AutoCapability cap_search(CAP_DAC_READ_SEARCH, false); - AutoCapability cap_override(CAP_DAC_OVERRIDE, false); - - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); - EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallFailsWithErrno(EACCES)); -} - -TEST(ChrootTest, NotDir) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - EXPECT_THAT(chroot(temp_file.path().c_str()), SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(ChrootTest, NotExist) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - EXPECT_THAT(chroot("/foo/bar"), SyscallFailsWithErrno(ENOENT)); -} - -TEST(ChrootTest, WithoutCapability) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETPCAP))); - - // Unset CAP_SYS_CHROOT. - AutoCapability cap(CAP_SYS_CHROOT, false); - - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(chroot(temp_dir.path().c_str()), SyscallFailsWithErrno(EPERM)); -} - -TEST(ChrootTest, CreatesNewRoot) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - // Grab the initial cwd. - char initial_cwd[1024]; - ASSERT_THAT(syscall(__NR_getcwd, initial_cwd, sizeof(initial_cwd)), - SyscallSucceeds()); - - auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto file_in_new_root = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(new_root.path())); - - const auto rest = [&] { - // chroot into new_root. - TEST_CHECK_SUCCESS(chroot(new_root.path().c_str())); - - // getcwd should return "(unreachable)" followed by the initial_cwd. - char cwd[1024]; - TEST_CHECK_SUCCESS(syscall(__NR_getcwd, cwd, sizeof(cwd))); - std::string expected_cwd = "(unreachable)"; - expected_cwd += initial_cwd; - TEST_CHECK(strcmp(cwd, expected_cwd.c_str()) == 0); - - // Should not be able to stat file by its full path. - struct stat statbuf; - TEST_CHECK_ERRNO(stat(file_in_new_root.path().c_str(), &statbuf), ENOENT); - - // Should be able to stat file at new rooted path. - auto basename = std::string(Basename(file_in_new_root.path())); - auto rootedFile = "/" + basename; - TEST_CHECK_SUCCESS(stat(rootedFile.c_str(), &statbuf)); - - // Should be able to stat cwd at '.' even though it's outside root. - TEST_CHECK_SUCCESS(stat(".", &statbuf)); - - // chdir into new root. - TEST_CHECK_SUCCESS(chdir("/")); - - // getcwd should return "/". - TEST_CHECK_SUCCESS(syscall(__NR_getcwd, cwd, sizeof(cwd))); - TEST_CHECK_SUCCESS(strcmp(cwd, "/") == 0); - - // Statting '.', '..', '/', and '/..' all return the same dev and inode. - struct stat statbuf_dot; - TEST_CHECK_SUCCESS(stat(".", &statbuf_dot)); - struct stat statbuf_dotdot; - TEST_CHECK_SUCCESS(stat("..", &statbuf_dotdot)); - TEST_CHECK(statbuf_dot.st_dev == statbuf_dotdot.st_dev); - TEST_CHECK(statbuf_dot.st_ino == statbuf_dotdot.st_ino); - struct stat statbuf_slash; - TEST_CHECK_SUCCESS(stat("/", &statbuf_slash)); - TEST_CHECK(statbuf_dot.st_dev == statbuf_slash.st_dev); - TEST_CHECK(statbuf_dot.st_ino == statbuf_slash.st_ino); - struct stat statbuf_slashdotdot; - TEST_CHECK_SUCCESS(stat("/..", &statbuf_slashdotdot)); - TEST_CHECK(statbuf_dot.st_dev == statbuf_slashdotdot.st_dev); - TEST_CHECK(statbuf_dot.st_ino == statbuf_slashdotdot.st_ino); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST(ChrootTest, DotDotFromOpenFD) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - auto dir_outside_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(dir_outside_root.path(), O_RDONLY | O_DIRECTORY)); - auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - const auto rest = [&] { - // chroot into new_root. - TEST_CHECK_SUCCESS(chroot(new_root.path().c_str())); - - // openat on fd with path .. will succeed. - int other_fd; - TEST_CHECK_SUCCESS(other_fd = openat(fd.get(), "..", O_RDONLY)); - TEST_CHECK_SUCCESS(close(other_fd)); - - // getdents on fd should not error. - char buf[1024]; - TEST_CHECK_SUCCESS(syscall(SYS_getdents64, fd.get(), buf, sizeof(buf))); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -// Test that link resolution in a chroot can escape the root by following an -// open proc fd. Regression test for b/32316719. -TEST(ChrootTest, ProcFdLinkResolutionInChroot) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - const TempPath file_outside_chroot = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file_outside_chroot.path(), O_RDONLY)); - - const FileDescriptor proc_fd = ASSERT_NO_ERRNO_AND_VALUE( - Open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)); - - const auto rest = [&] { - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str())); - - // Opening relative to an already open fd to a node outside the chroot - // works. - const FileDescriptor proc_self_fd = TEST_CHECK_NO_ERRNO_AND_VALUE( - OpenAt(proc_fd.get(), "self/fd", O_DIRECTORY | O_RDONLY | O_CLOEXEC)); - - // Proc fd symlinks can escape the chroot if the fd the symlink refers to - // refers to an object outside the chroot. - struct stat s = {}; - TEST_CHECK_SUCCESS( - fstatat(proc_self_fd.get(), absl::StrCat(fd.get()).c_str(), &s, 0)); - - // Try to stat the stdin fd. Internally, this is handled differently from a - // proc fd entry pointing to a file, since stdin is backed by a host fd, and - // isn't a walkable path on the filesystem inside the sandbox. - TEST_CHECK_SUCCESS(fstatat(proc_self_fd.get(), "0", &s, 0)); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -// This test will verify that when you hold a fd to proc before entering -// a chroot that any files inside the chroot will appear rooted to the -// base chroot when examining /proc/self/fd/{num}. -TEST(ChrootTest, ProcMemSelfFdsNoEscapeProcOpen) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - // Get a FD to /proc before we enter the chroot. - const FileDescriptor proc = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY)); - - const auto rest = [&] { - // Create and enter a chroot directory. - const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str())); - - // Open a file inside the chroot at /foo. - const FileDescriptor foo = - TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644)); - - // Examine /proc/self/fd/{foo_fd} to see if it exposes the fact that we're - // inside a chroot, the path should be /foo and NOT {chroot_dir}/foo. - const std::string fd_path = absl::StrCat("self/fd/", foo.get()); - char buf[1024] = {}; - size_t bytes_read = 0; - TEST_CHECK_SUCCESS(bytes_read = readlinkat(proc.get(), fd_path.c_str(), buf, - sizeof(buf) - 1)); - - // The link should resolve to something. - TEST_CHECK(bytes_read > 0); - - // Assert that the link doesn't contain the chroot path and is only /foo. - TEST_CHECK(strcmp(buf, "/foo") == 0); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -// This test will verify that a file inside a chroot when mmapped will not -// expose the full file path via /proc/self/maps and instead honor the chroot. -TEST(ChrootTest, ProcMemSelfMapsNoEscapeProcOpen) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - // Get a FD to /proc before we enter the chroot. - const FileDescriptor proc = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY)); - - const auto rest = [&] { - // Create and enter a chroot directory. - const auto temp_dir = TEST_CHECK_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str())); - - // Open a file inside the chroot at /foo. - const FileDescriptor foo = - TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/foo", O_CREAT | O_RDONLY, 0644)); - - // Mmap the newly created file. - void* foo_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE, foo.get(), 0); - TEST_CHECK_SUCCESS(reinterpret_cast<int64_t>(foo_map)); - - // Always unmap. - auto cleanup_map = - Cleanup([&] { TEST_CHECK_SUCCESS(munmap(foo_map, kPageSize)); }); - - // Examine /proc/self/maps to be sure that /foo doesn't appear to be - // mapped with the full chroot path. - const FileDescriptor maps = TEST_CHECK_NO_ERRNO_AND_VALUE( - OpenAt(proc.get(), "self/maps", O_RDONLY)); - - size_t bytes_read = 0; - char buf[8 * 1024] = {}; - TEST_CHECK_SUCCESS(bytes_read = ReadFd(maps.get(), buf, sizeof(buf))); - - // The maps file should have something. - TEST_CHECK(bytes_read > 0); - - // Finally we want to make sure the maps don't contain the chroot path - TEST_CHECK(std::string(buf, bytes_read).find(temp_dir.path()) == - std::string::npos); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -// Test that mounts outside the chroot will not appear in /proc/self/mounts or -// /proc/self/mountinfo. -TEST(ChrootTest, ProcMountsMountinfoNoEscape) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT))); - - // Create nested tmpfs mounts. - auto const outer_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const outer_mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("none", outer_dir.path(), "tmpfs", 0, "mode=0700", 0)); - - auto const inner_dir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outer_dir.path())); - auto const inner_mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("none", inner_dir.path(), "tmpfs", 0, "mode=0700", 0)); - - const auto rest = [&outer_dir, &inner_dir] { - // Filenames that will be checked for mounts, all relative to /proc dir. - std::string paths[3] = {"mounts", "self/mounts", "self/mountinfo"}; - - for (const std::string& path : paths) { - // We should have both inner and outer mounts. - const std::string contents = - TEST_CHECK_NO_ERRNO_AND_VALUE(GetContents(JoinPath("/proc", path))); - EXPECT_THAT(contents, AllOf(HasSubstr(outer_dir.path()), - HasSubstr(inner_dir.path()))); - // We better have at least two mounts: the mounts we created plus the - // root. - std::vector<absl::string_view> submounts = - absl::StrSplit(contents, '\n', absl::SkipWhitespace()); - TEST_CHECK(submounts.size() > 2); - } - - // Get a FD to /proc before we enter the chroot. - const FileDescriptor proc = - TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY)); - - // Chroot to outer mount. - TEST_CHECK_SUCCESS(chroot(outer_dir.path().c_str())); - - for (const std::string& path : paths) { - const FileDescriptor proc_file = - TEST_CHECK_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY)); - - // Only two mounts visible from this chroot: the inner and outer. Both - // paths should be relative to the new chroot. - const std::string contents = - TEST_CHECK_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get())); - EXPECT_THAT(contents, - AllOf(HasSubstr(absl::StrCat(Basename(inner_dir.path()))), - Not(HasSubstr(outer_dir.path())), - Not(HasSubstr(inner_dir.path())))); - std::vector<absl::string_view> submounts = - absl::StrSplit(contents, '\n', absl::SkipWhitespace()); - TEST_CHECK(submounts.size() == 2); - } - - // Chroot to inner mount. We must use an absolute path accessible to our - // chroot. - const std::string inner_dir_basename = - absl::StrCat("/", Basename(inner_dir.path())); - TEST_CHECK_SUCCESS(chroot(inner_dir_basename.c_str())); - - for (const std::string& path : paths) { - const FileDescriptor proc_file = - TEST_CHECK_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY)); - const std::string contents = - TEST_CHECK_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get())); - - // Only the inner mount visible from this chroot. - std::vector<absl::string_view> submounts = - absl::StrSplit(contents, '\n', absl::SkipWhitespace()); - TEST_CHECK(submounts.size() == 1); - } - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/clock_getres.cc b/test/syscalls/linux/clock_getres.cc deleted file mode 100644 index c408b936c..000000000 --- a/test/syscalls/linux/clock_getres.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/time.h> -#include <time.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// clock_getres works regardless of whether or not a timespec is passed. -TEST(ClockGetres, Timespec) { - struct timespec ts; - EXPECT_THAT(clock_getres(CLOCK_MONOTONIC, &ts), SyscallSucceeds()); - EXPECT_THAT(clock_getres(CLOCK_MONOTONIC, nullptr), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/clock_gettime.cc b/test/syscalls/linux/clock_gettime.cc deleted file mode 100644 index 7f6015049..000000000 --- a/test/syscalls/linux/clock_gettime.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <pthread.h> -#include <sys/time.h> - -#include <cerrno> -#include <cstdint> -#include <ctime> -#include <list> -#include <memory> -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -int64_t clock_gettime_nsecs(clockid_t id) { - struct timespec ts; - TEST_PCHECK(clock_gettime(id, &ts) == 0); - return (ts.tv_sec * 1000000000 + ts.tv_nsec); -} - -// Spin on the CPU for at least ns nanoseconds, based on -// CLOCK_THREAD_CPUTIME_ID. -void spin_ns(int64_t ns) { - int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID); - int64_t end = start + ns; - - do { - constexpr int kLoopCount = 1000000; // large and arbitrary - // volatile to prevent the compiler from skipping this loop. - for (volatile int i = 0; i < kLoopCount; i++) { - } - } while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end); -} - -// Test that CLOCK_PROCESS_CPUTIME_ID is a superset of CLOCK_THREAD_CPUTIME_ID. -TEST(ClockGettime, CputimeId) { - constexpr int kNumThreads = 13; // arbitrary - - absl::Duration spin_time = absl::Seconds(1); - - // Start off the worker threads and compute the aggregate time spent by - // the workers. Note that we test CLOCK_PROCESS_CPUTIME_ID by having the - // workers execute in parallel and verifying that CLOCK_PROCESS_CPUTIME_ID - // accumulates the runtime of all threads. - int64_t start = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID); - - // Create a kNumThreads threads. - std::list<ScopedThread> threads; - for (int i = 0; i < kNumThreads; i++) { - threads.emplace_back( - [spin_time] { spin_ns(absl::ToInt64Nanoseconds(spin_time)); }); - } - for (auto& t : threads) { - t.Join(); - } - - int64_t end = clock_gettime_nsecs(CLOCK_PROCESS_CPUTIME_ID); - - // The aggregate time spent in the worker threads must be at least - // 'kNumThreads' times the time each thread spun. - ASSERT_GE(end - start, kNumThreads * absl::ToInt64Nanoseconds(spin_time)); -} - -TEST(ClockGettime, JavaThreadTime) { - clockid_t clockid; - ASSERT_EQ(0, pthread_getcpuclockid(pthread_self(), &clockid)); - struct timespec tp; - ASSERT_THAT(clock_getres(clockid, &tp), SyscallSucceeds()); - EXPECT_TRUE(tp.tv_sec > 0 || tp.tv_nsec > 0); - // A thread cputime is updated each 10msec and there is no approximation - // if a task is running. - do { - ASSERT_THAT(clock_gettime(clockid, &tp), SyscallSucceeds()); - } while (tp.tv_sec == 0 && tp.tv_nsec == 0); - EXPECT_TRUE(tp.tv_sec > 0 || tp.tv_nsec > 0); -} - -// There is not much to test here, since CLOCK_REALTIME may be discontiguous. -TEST(ClockGettime, RealtimeWorks) { - struct timespec tp; - EXPECT_THAT(clock_gettime(CLOCK_REALTIME, &tp), SyscallSucceeds()); -} - -class MonotonicClockTest : public ::testing::TestWithParam<clockid_t> {}; - -TEST_P(MonotonicClockTest, IsMonotonic) { - auto end = absl::Now() + absl::Seconds(5); - - struct timespec tp; - EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds()); - - auto prev = absl::TimeFromTimespec(tp); - while (absl::Now() < end) { - EXPECT_THAT(clock_gettime(GetParam(), &tp), SyscallSucceeds()); - auto now = absl::TimeFromTimespec(tp); - EXPECT_GE(now, prev); - prev = now; - } -} - -std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) { - switch (info.param) { - case CLOCK_MONOTONIC: - return "CLOCK_MONOTONIC"; - case CLOCK_MONOTONIC_COARSE: - return "CLOCK_MONOTONIC_COARSE"; - case CLOCK_MONOTONIC_RAW: - return "CLOCK_MONOTONIC_RAW"; - case CLOCK_BOOTTIME: - // CLOCK_BOOTTIME is a monotonic clock. - return "CLOCK_BOOTTIME"; - default: - return absl::StrCat(info.param); - } -} - -INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicClockTest, - ::testing::Values(CLOCK_MONOTONIC, - CLOCK_MONOTONIC_COARSE, - CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME), - PrintClockId); - -TEST(ClockGettime, UnimplementedReturnsEINVAL) { - SKIP_IF(!IsRunningOnGvisor()); - - struct timespec tp; - EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(ClockGettime, InvalidClockIDReturnsEINVAL) { - struct timespec tp; - EXPECT_THAT(clock_gettime(-1, &tp), SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/clock_nanosleep.cc b/test/syscalls/linux/clock_nanosleep.cc deleted file mode 100644 index b55cddc52..000000000 --- a/test/syscalls/linux/clock_nanosleep.cc +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <time.h> - -#include <atomic> -#include <utility> - -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// sys_clock_nanosleep is defined because the glibc clock_nanosleep returns -// error numbers directly and does not set errno. This makes our Syscall -// matchers look a little weird when expecting failure: -// "SyscallSucceedsWithValue(ERRNO)". -int sys_clock_nanosleep(clockid_t clkid, int flags, - const struct timespec* request, - struct timespec* remain) { - return syscall(SYS_clock_nanosleep, clkid, flags, request, remain); -} - -PosixErrorOr<absl::Time> GetTime(clockid_t clk) { - struct timespec ts = {}; - const int rc = clock_gettime(clk, &ts); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "clock_gettime"); - } - return absl::TimeFromTimespec(ts); -} - -class WallClockNanosleepTest : public ::testing::TestWithParam<clockid_t> {}; - -TEST_P(WallClockNanosleepTest, InvalidValues) { - const struct timespec invalid[] = { - {.tv_sec = -1, .tv_nsec = -1}, {.tv_sec = 0, .tv_nsec = INT32_MIN}, - {.tv_sec = 0, .tv_nsec = INT32_MAX}, {.tv_sec = 0, .tv_nsec = -1}, - {.tv_sec = -1, .tv_nsec = 0}, - }; - - for (auto const ts : invalid) { - EXPECT_THAT(sys_clock_nanosleep(GetParam(), 0, &ts, nullptr), - SyscallFailsWithErrno(EINVAL)); - } -} - -TEST_P(WallClockNanosleepTest, SleepOneSecond) { - constexpr absl::Duration kSleepDuration = absl::Seconds(1); - struct timespec duration = absl::ToTimespec(kSleepDuration); - - const absl::Time before = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); - EXPECT_THAT( - RetryEINTR(sys_clock_nanosleep)(GetParam(), 0, &duration, &duration), - SyscallSucceeds()); - const absl::Time after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); - - EXPECT_GE(after - before, kSleepDuration); -} - -TEST_P(WallClockNanosleepTest, InterruptedNanosleep) { - constexpr absl::Duration kSleepDuration = absl::Seconds(60); - struct timespec duration = absl::ToTimespec(kSleepDuration); - - // Install no-op signal handler for SIGALRM. - struct sigaction sa = {}; - sigfillset(&sa.sa_mask); - sa.sa_handler = +[](int signo) {}; - const auto cleanup_sa = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); - - // Measure time since setting the alarm, since the alarm will interrupt the - // sleep and hence determine how long we sleep. - const absl::Time before = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); - - // Set an alarm to go off while sleeping. - struct itimerval timer = {}; - timer.it_value.tv_sec = 1; - timer.it_value.tv_usec = 0; - timer.it_interval.tv_sec = 1; - timer.it_interval.tv_usec = 0; - const auto cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, timer)); - - EXPECT_THAT(sys_clock_nanosleep(GetParam(), 0, &duration, &duration), - SyscallFailsWithErrno(EINTR)); - const absl::Time after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); - - // Remaining time updated. - const absl::Duration remaining = absl::DurationFromTimespec(duration); - EXPECT_GE(after - before + remaining, kSleepDuration); -} - -// Remaining time is *not* updated if nanosleep completes uninterrupted. -TEST_P(WallClockNanosleepTest, UninterruptedNanosleep) { - constexpr absl::Duration kSleepDuration = absl::Milliseconds(10); - const struct timespec duration = absl::ToTimespec(kSleepDuration); - - while (true) { - constexpr int kRemainingMagic = 42; - struct timespec remaining; - remaining.tv_sec = kRemainingMagic; - remaining.tv_nsec = kRemainingMagic; - - int ret = sys_clock_nanosleep(GetParam(), 0, &duration, &remaining); - if (ret == EINTR) { - // Retry from beginning. We want a single uninterrupted call. - continue; - } - - EXPECT_THAT(ret, SyscallSucceeds()); - EXPECT_EQ(remaining.tv_sec, kRemainingMagic); - EXPECT_EQ(remaining.tv_nsec, kRemainingMagic); - break; - } -} - -TEST_P(WallClockNanosleepTest, SleepUntil) { - const absl::Time now = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); - const absl::Time until = now + absl::Seconds(2); - const struct timespec ts = absl::ToTimespec(until); - - EXPECT_THAT( - RetryEINTR(sys_clock_nanosleep)(GetParam(), TIMER_ABSTIME, &ts, nullptr), - SyscallSucceeds()); - const absl::Time after = ASSERT_NO_ERRNO_AND_VALUE(GetTime(GetParam())); - - EXPECT_GE(after, until); -} - -INSTANTIATE_TEST_SUITE_P(Sleepers, WallClockNanosleepTest, - ::testing::Values(CLOCK_REALTIME, CLOCK_MONOTONIC)); - -TEST(ClockNanosleepProcessTest, SleepFiveSeconds) { - const absl::Duration kSleepDuration = absl::Seconds(5); - struct timespec duration = absl::ToTimespec(kSleepDuration); - - // Ensure that CLOCK_PROCESS_CPUTIME_ID advances. - std::atomic<bool> done(false); - ScopedThread t([&] { - while (!done.load()) { - } - }); - const auto cleanup_done = Cleanup([&] { done.store(true); }); - - const absl::Time before = - ASSERT_NO_ERRNO_AND_VALUE(GetTime(CLOCK_PROCESS_CPUTIME_ID)); - EXPECT_THAT(RetryEINTR(sys_clock_nanosleep)(CLOCK_PROCESS_CPUTIME_ID, 0, - &duration, &duration), - SyscallSucceeds()); - const absl::Time after = - ASSERT_NO_ERRNO_AND_VALUE(GetTime(CLOCK_PROCESS_CPUTIME_ID)); - EXPECT_GE(after - before, kSleepDuration); -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/concurrency.cc b/test/syscalls/linux/concurrency.cc deleted file mode 100644 index 7cd6a75bd..000000000 --- a/test/syscalls/linux/concurrency.cc +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> - -#include <atomic> - -#include "gtest/gtest.h" -#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" - -namespace gvisor { -namespace testing { -namespace { - -// Test that a thread that never yields to the OS does not prevent other threads -// from running. -TEST(ConcurrencyTest, SingleProcessMultithreaded) { - std::atomic<int> a(0); - - ScopedThread t([&a]() { - while (!a.load()) { - } - }); - - absl::SleepFor(absl::Seconds(1)); - - // We are still able to execute code in this thread. The other hasn't - // permanently hung execution in both threads. - a.store(1); -} - -// Test that multiple threads in this process continue to execute in parallel, -// even if an unrelated second process is spawned. Regression test for -// b/32119508. -TEST(ConcurrencyTest, MultiProcessMultithreaded) { - // In PID 1, start TIDs 1 and 2, and put both to sleep. - // - // Start PID 3, which spins for 5 seconds, then exits. - // - // TIDs 1 and 2 wake and attempt to Activate, which cannot occur until PID 3 - // exits. - // - // Both TIDs 1 and 2 should be woken. If they are not both woken, the test - // hangs. - // - // This is all fundamentally racy. If we are failing to wake all threads, the - // expectation is that this test becomes flaky, rather than consistently - // failing. - // - // If additional background threads fail to block, we may never schedule the - // child, at which point this test effectively becomes - // MultiProcessConcurrency. That's not expected to occur. - - std::atomic<int> a(0); - ScopedThread t([&a]() { - // Block so that PID 3 can execute and we can wait on its exit. - absl::SleepFor(absl::Seconds(1)); - while (!a.load()) { - } - }); - - pid_t child_pid = fork(); - if (child_pid == 0) { - // Busy wait without making any blocking syscalls. - auto end = absl::Now() + absl::Seconds(5); - while (absl::Now() < end) { - } - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - - absl::SleepFor(absl::Seconds(1)); - - // If only TID 1 is woken, thread.Join will hang. - // If only TID 2 is woken, both will hang. - a.store(1); - t.Join(); - - int status = 0; - EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(WEXITSTATUS(status), 0); -} - -// 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) { - while (true) { - } - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - - absl::SleepFor(absl::Seconds(5)); - - // We are still able to execute code in this process. The other hasn't - // permanently hung execution in both processes. - ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); - int status = 0; - - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - ASSERT_TRUE(WIFSIGNALED(status)); - ASSERT_EQ(WTERMSIG(status), SIGKILL); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/connect_external.cc b/test/syscalls/linux/connect_external.cc deleted file mode 100644 index 1edb50e47..000000000 --- a/test/syscalls/linux/connect_external.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <stdlib.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <string> -#include <tuple> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/test_util.h" - -// This file contains tests specific to connecting to host UDS managed outside -// the sandbox / test. -// -// A set of ultity sockets will be created externally in $TEST_UDS_TREE and -// $TEST_UDS_ATTACH_TREE for these tests to interact with. - -namespace gvisor { -namespace testing { - -namespace { - -struct ProtocolSocket { - int protocol; - std::string name; -}; - -// Parameter is (socket root dir, ProtocolSocket). -using GoferStreamSeqpacketTest = - ::testing::TestWithParam<std::tuple<std::string, ProtocolSocket>>; - -// Connect to a socket and verify that write/read work. -// -// An "echo" socket doesn't work for dgram sockets because our socket is -// unnamed. The server thus has no way to reply to us. -TEST_P(GoferStreamSeqpacketTest, Echo) { - std::string env; - ProtocolSocket proto; - std::tie(env, proto) = GetParam(); - - char* val = getenv(env.c_str()); - ASSERT_NE(val, nullptr); - std::string root(val); - - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, proto.protocol, 0)); - - std::string socket_path = JoinPath(root, proto.name, "echo"); - - struct sockaddr_un addr = {}; - 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), - sizeof(addr)), - SyscallSucceeds()); - - constexpr int kBufferSize = 64; - char send_buffer[kBufferSize]; - memset(send_buffer, 'a', sizeof(send_buffer)); - - ASSERT_THAT(WriteFd(sock.get(), send_buffer, sizeof(send_buffer)), - SyscallSucceedsWithValue(sizeof(send_buffer))); - - char recv_buffer[kBufferSize]; - ASSERT_THAT(ReadFd(sock.get(), recv_buffer, sizeof(recv_buffer)), - SyscallSucceedsWithValue(sizeof(recv_buffer))); - ASSERT_EQ(0, memcmp(send_buffer, recv_buffer, sizeof(send_buffer))); -} - -// It is not possible to connect to a bound but non-listening socket. -TEST_P(GoferStreamSeqpacketTest, NonListening) { - std::string env; - ProtocolSocket proto; - std::tie(env, proto) = GetParam(); - - char* val = getenv(env.c_str()); - ASSERT_NE(val, nullptr); - std::string root(val); - - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, proto.protocol, 0)); - - std::string socket_path = JoinPath(root, proto.name, "nonlistening"); - - struct sockaddr_un addr = {}; - 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), - sizeof(addr)), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -INSTANTIATE_TEST_SUITE_P( - StreamSeqpacket, GoferStreamSeqpacketTest, - ::testing::Combine( - // Test access via standard path and attach point. - ::testing::Values("TEST_UDS_TREE", "TEST_UDS_ATTACH_TREE"), - ::testing::Values(ProtocolSocket{SOCK_STREAM, "stream"}, - ProtocolSocket{SOCK_SEQPACKET, "seqpacket"}))); - -// Parameter is socket root dir. -using GoferDgramTest = ::testing::TestWithParam<std::string>; - -// Connect to a socket and verify that write works. -// -// An "echo" socket doesn't work for dgram sockets because our socket is -// 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()); - ASSERT_NE(val, nullptr); - std::string root(val); - - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_DGRAM, 0)); - - std::string socket_path = JoinPath(root, "dgram/null"); - - struct sockaddr_un addr = {}; - 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), - sizeof(addr)), - SyscallSucceeds()); - - constexpr int kBufferSize = 64; - char send_buffer[kBufferSize]; - memset(send_buffer, 'a', sizeof(send_buffer)); - - ASSERT_THAT(WriteFd(sock.get(), send_buffer, sizeof(send_buffer)), - SyscallSucceedsWithValue(sizeof(send_buffer))); -} - -INSTANTIATE_TEST_SUITE_P(Dgram, GoferDgramTest, - // Test access via standard path and attach point. - ::testing::Values("TEST_UDS_TREE", - "TEST_UDS_ATTACH_TREE")); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/creat.cc b/test/syscalls/linux/creat.cc deleted file mode 100644 index 3c270d6da..000000000 --- a/test/syscalls/linux/creat.cc +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> - -#include <string> - -#include "gtest/gtest.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr int kMode = 0666; - -TEST(CreatTest, CreatCreatesNewFile) { - std::string const path = NewTempAbsPath(); - struct stat buf; - int fd; - ASSERT_THAT(stat(path.c_str(), &buf), SyscallFailsWithErrno(ENOENT)); - ASSERT_THAT(fd = creat(path.c_str(), kMode), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - EXPECT_THAT(stat(path.c_str(), &buf), SyscallSucceeds()); -} - -TEST(CreatTest, CreatTruncatesExistingFile) { - auto temp_path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - int fd; - ASSERT_NO_ERRNO(SetContents(temp_path.path(), "non-empty")); - ASSERT_THAT(fd = creat(temp_path.path().c_str(), kMode), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - std::string new_contents; - ASSERT_NO_ERRNO(GetContents(temp_path.path(), &new_contents)); - EXPECT_EQ("", new_contents); -} - -TEST(CreatTest, CreatWithNameTooLong) { - // Start with a unique name, and pad it to NAME_MAX + 1; - std::string name = NewTempRelPath(); - int padding = (NAME_MAX + 1) - name.size(); - name.append(padding, 'x'); - const std::string& path = JoinPath(GetAbsoluteTestTmpdir(), name); - - // Creation should return ENAMETOOLONG. - ASSERT_THAT(creat(path.c_str(), kMode), SyscallFailsWithErrno(ENAMETOOLONG)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/dev.cc b/test/syscalls/linux/dev.cc deleted file mode 100644 index 1d0d584cd..000000000 --- a/test/syscalls/linux/dev.cc +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(DevTest, LseekDevUrandom) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/urandom", O_RDONLY)); - EXPECT_THAT(lseek(fd.get(), -10, SEEK_CUR), SyscallSucceeds()); - EXPECT_THAT(lseek(fd.get(), -10, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); -} - -TEST(DevTest, LseekDevNull) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); - EXPECT_THAT(lseek(fd.get(), -10, SEEK_CUR), SyscallSucceeds()); - EXPECT_THAT(lseek(fd.get(), -10, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); -} - -TEST(DevTest, LseekDevZero) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); -} - -TEST(DevTest, LseekDevFull) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/full", O_RDONLY)); - EXPECT_THAT(lseek(fd.get(), 123, SEEK_SET), SyscallSucceedsWithValue(0)); - EXPECT_THAT(lseek(fd.get(), 123, SEEK_CUR), SyscallSucceedsWithValue(0)); - EXPECT_THAT(lseek(fd.get(), 123, SEEK_END), SyscallSucceedsWithValue(0)); -} - -TEST(DevTest, LseekDevNullFreshFile) { - // Seeks to /dev/null always return 0. - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); - - EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - EXPECT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceedsWithValue(0)); - EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - const FileDescriptor fd3 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); - EXPECT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); -} - -TEST(DevTest, OpenTruncate) { - // Truncation is ignored on linux and gvisor for device files. - ASSERT_NO_ERRNO_AND_VALUE( - Open("/dev/null", O_CREAT | O_TRUNC | O_WRONLY, 0644)); - ASSERT_NO_ERRNO_AND_VALUE( - Open("/dev/zero", O_CREAT | O_TRUNC | O_WRONLY, 0644)); - ASSERT_NO_ERRNO_AND_VALUE( - Open("/dev/full", O_CREAT | O_TRUNC | O_WRONLY, 0644)); -} - -TEST(DevTest, Pread64DevNull) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); - char buf[1]; - EXPECT_THAT(pread64(fd.get(), buf, 1, 0), SyscallSucceedsWithValue(0)); -} - -TEST(DevTest, Pread64DevZero) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); - char buf[1]; - EXPECT_THAT(pread64(fd.get(), buf, 1, 0), SyscallSucceedsWithValue(1)); -} - -TEST(DevTest, Pread64DevFull) { - // /dev/full behaves like /dev/zero with respect to reads. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/full", O_RDONLY)); - char buf[1]; - EXPECT_THAT(pread64(fd.get(), buf, 1, 0), SyscallSucceedsWithValue(1)); -} - -TEST(DevTest, ReadDevNull) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDONLY)); - std::vector<char> buf(1); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), 1), SyscallSucceeds()); -} - -// Do not allow random save as it could lead to partial reads. -TEST(DevTest, ReadDevZero_NoRandomSave) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); - - constexpr int kReadSize = 128 * 1024; - std::vector<char> buf(kReadSize, 1); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), kReadSize), - SyscallSucceedsWithValue(kReadSize)); - EXPECT_EQ(std::vector<char>(kReadSize, 0), buf); -} - -TEST(DevTest, WriteDevNull) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_WRONLY)); - EXPECT_THAT(WriteFd(fd.get(), "a", 1), SyscallSucceedsWithValue(1)); -} - -TEST(DevTest, WriteDevZero) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY)); - EXPECT_THAT(WriteFd(fd.get(), "a", 1), SyscallSucceedsWithValue(1)); -} - -TEST(DevTest, WriteDevFull) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/full", O_WRONLY)); - EXPECT_THAT(WriteFd(fd.get(), "a", 1), SyscallFailsWithErrno(ENOSPC)); -} - -TEST(DevTest, TTYExists) { - struct stat statbuf = {}; - ASSERT_THAT(stat("/dev/tty", &statbuf), SyscallSucceeds()); - // Check that it's a character device with rw-rw-rw- permissions. - EXPECT_EQ(statbuf.st_mode, S_IFCHR | 0666); -} - -TEST(DevTest, OpenDevFuse) { - // Note(gvisor.dev/issue/3076) This won't work in the sentry until the new - // device registration is complete. - SKIP_IF(IsRunningWithVFS1() || IsRunningOnGvisor() || !IsFUSEEnabled()); - - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/fuse", O_RDONLY)); -} - -TEST(DevTest, ReadDevFuseWithoutMount) { - // Note(gvisor.dev/issue/3076) This won't work in the sentry until the new - // device registration is complete. - SKIP_IF(IsRunningWithVFS1() || IsRunningOnGvisor()); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/fuse", O_RDONLY)); - - std::vector<char> buf(1); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), sizeof(buf)), - SyscallFailsWithErrno(EPERM)); -} - -} // namespace -} // namespace testing - -} // namespace gvisor diff --git a/test/syscalls/linux/dup.cc b/test/syscalls/linux/dup.cc deleted file mode 100644 index ba4e13fb9..000000000 --- a/test/syscalls/linux/dup.cc +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -PosixErrorOr<FileDescriptor> Dup2(const FileDescriptor& fd, int target_fd) { - int new_fd = dup2(fd.get(), target_fd); - if (new_fd < 0) { - return PosixError(errno, "Dup2"); - } - return FileDescriptor(new_fd); -} - -PosixErrorOr<FileDescriptor> Dup3(const FileDescriptor& fd, int target_fd, - int flags) { - int new_fd = dup3(fd.get(), target_fd, flags); - if (new_fd < 0) { - return PosixError(errno, "Dup2"); - } - return FileDescriptor(new_fd); -} - -TEST(DupTest, Dup) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Dup the descriptor and make sure it's the same file. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); -} - -TEST(DupTest, DupClearsCloExec) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_CLOEXEC)); - EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - - // Duplicate the descriptor. Ensure that it doesn't have FD_CLOEXEC set. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); -} - -TEST(DupTest, DupWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); - int flags; - ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - - // Dup the descriptor and make sure it's the same file. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); -} - -TEST(DupTest, Dup2) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Regular dup once. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - - // Dup over the file above. - int target_fd = nfd.release(); - FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd)); - EXPECT_EQ(target_fd, nfd2.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2)); -} - -TEST(DupTest, Dup2SameFD) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Should succeed. - ASSERT_THAT(dup2(fd.get(), fd.get()), SyscallSucceedsWithValue(fd.get())); -} - -TEST(DupTest, Dup2WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); - int flags; - ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - - // Regular dup once. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); - - // Dup over the file above. - int target_fd = nfd.release(); - FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd)); - EXPECT_EQ(target_fd, nfd2.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2)); - EXPECT_THAT(fcntl(nfd2.get(), F_GETFL), SyscallSucceedsWithValue(flags)); -} - -TEST(DupTest, Dup3) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Regular dup once. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - - // Dup over the file above, check that it has no CLOEXEC. - nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0)); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Dup over the file again, check that it does not CLOEXEC. - nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC)); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(DupTest, Dup3FailsSameFD) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Only dup3 fails if the new and old fd are the same. - ASSERT_THAT(dup3(fd.get(), fd.get(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(DupTest, Dup3WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); - EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - int flags; - ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - - // Regular dup once. - FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - - // Dup over the file above, check that it has no CLOEXEC. - nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0)); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); - - // Dup over the file again, check that it does not CLOEXEC. - nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC)); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc deleted file mode 100644 index 8a72ef10a..000000000 --- a/test/syscalls/linux/epoll.cc +++ /dev/null @@ -1,445 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <limits.h> -#include <pthread.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/eventfd.h> -#include <time.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/epoll_util.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr int kFDsPerEpoll = 3; -constexpr uint64_t kMagicConstant = 0x0102030405060708; - -TEST(EpollTest, AllWritable) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), - EPOLLIN | EPOLLOUT, kMagicConstant + i)); - } - - struct epoll_event result[kFDsPerEpoll]; - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(kFDsPerEpoll)); - for (int i = 0; i < kFDsPerEpoll; i++) { - ASSERT_EQ(result[i].events, EPOLLOUT); - } -} - -TEST(EpollTest, LastReadable) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), - EPOLLIN | EPOLLOUT, kMagicConstant + i)); - } - - uint64_t tmp = 1; - ASSERT_THAT(WriteFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)), - SyscallSucceedsWithValue(sizeof(tmp))); - - struct epoll_event result[kFDsPerEpoll]; - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(kFDsPerEpoll)); - - int i; - for (i = 0; i < kFDsPerEpoll - 1; i++) { - EXPECT_EQ(result[i].events, EPOLLOUT); - } - EXPECT_EQ(result[i].events, EPOLLOUT | EPOLLIN); - EXPECT_EQ(result[i].data.u64, kMagicConstant + i); -} - -TEST(EpollTest, LastNonWritable) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), - EPOLLIN | EPOLLOUT, kMagicConstant + i)); - } - - // Write the maximum value to the event fd so that writing to it again would - // block. - uint64_t tmp = ULLONG_MAX - 1; - ASSERT_THAT(WriteFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)), - SyscallSucceedsWithValue(sizeof(tmp))); - - struct epoll_event result[kFDsPerEpoll]; - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(kFDsPerEpoll)); - - int i; - for (i = 0; i < kFDsPerEpoll - 1; i++) { - EXPECT_EQ(result[i].events, EPOLLOUT); - } - EXPECT_EQ(result[i].events, EPOLLIN); - EXPECT_THAT(ReadFd(eventfds[kFDsPerEpoll - 1].get(), &tmp, sizeof(tmp)), - sizeof(tmp)); - EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(kFDsPerEpoll)); - - for (i = 0; i < kFDsPerEpoll; i++) { - EXPECT_EQ(result[i].events, EPOLLOUT); - } -} - -TEST(EpollTest, Timeout_NoRandomSave) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, - kMagicConstant + i)); - } - - constexpr int kTimeoutMs = 200; - struct timespec begin; - struct timespec end; - struct epoll_event result[kFDsPerEpoll]; - - { - const DisableSave ds; // Timing-related. - EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &begin), SyscallSucceeds()); - - ASSERT_THAT( - RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, kTimeoutMs), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &end), SyscallSucceeds()); - } - - // Check the lower bound on the timeout. Checking for an upper bound is - // fragile because Linux can overrun the timeout due to scheduling delays. - EXPECT_GT(ms_elapsed(begin, end), kTimeoutMs - 1); -} - -void* writer(void* arg) { - int fd = *reinterpret_cast<int*>(arg); - uint64_t tmp = 1; - - usleep(200000); - if (WriteFd(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) { - fprintf(stderr, "writer failed: errno %s\n", strerror(errno)); - } - - return nullptr; -} - -TEST(EpollTest, WaitThenUnblock) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, - kMagicConstant + i)); - } - - // Fire off a thread that will make at least one of the event fds readable. - pthread_t thread; - int make_readable = eventfds[0].get(); - ASSERT_THAT(pthread_create(&thread, nullptr, writer, &make_readable), - SyscallSucceedsWithValue(0)); - - struct epoll_event result[kFDsPerEpoll]; - EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_THAT(pthread_detach(thread), SyscallSucceeds()); -} - -void sighandler(int s) {} - -void* signaler(void* arg) { - pthread_t* t = reinterpret_cast<pthread_t*>(arg); - // Repeatedly send the real-time signal until we are detached, because it's - // difficult to know exactly when epoll_wait on another thread (which this - // is intending to interrupt) has started blocking. - while (1) { - usleep(200000); - pthread_kill(*t, SIGRTMIN); - } - return nullptr; -} - -TEST(EpollTest, UnblockWithSignal) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, - kMagicConstant + i)); - } - - signal(SIGRTMIN, sighandler); - // Unblock the real time signals that InitGoogle blocks :( - sigset_t unblock; - sigemptyset(&unblock); - sigaddset(&unblock, SIGRTMIN); - ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &unblock, nullptr), SyscallSucceeds()); - - pthread_t thread; - pthread_t cur = pthread_self(); - ASSERT_THAT(pthread_create(&thread, nullptr, signaler, &cur), - SyscallSucceedsWithValue(0)); - - struct epoll_event result[kFDsPerEpoll]; - EXPECT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallFailsWithErrno(EINTR)); - EXPECT_THAT(pthread_cancel(thread), SyscallSucceeds()); - EXPECT_THAT(pthread_detach(thread), SyscallSucceeds()); -} - -TEST(EpollTest, TimeoutNoFds) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - struct epoll_event result[kFDsPerEpoll]; - EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); -} - -struct addr_ctx { - int epollfd; - int eventfd; -}; - -void* fd_adder(void* arg) { - struct addr_ctx* actx = reinterpret_cast<struct addr_ctx*>(arg); - struct epoll_event event; - event.events = EPOLLIN | EPOLLOUT; - event.data.u64 = 0xdeadbeeffacefeed; - - usleep(200000); - if (epoll_ctl(actx->epollfd, EPOLL_CTL_ADD, actx->eventfd, &event) == -1) { - fprintf(stderr, "epoll_ctl failed: %s\n", strerror(errno)); - } - - return nullptr; -} - -TEST(EpollTest, UnblockWithNewFD) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - - pthread_t thread; - struct addr_ctx actx = {epollfd.get(), eventfd.get()}; - ASSERT_THAT(pthread_create(&thread, nullptr, fd_adder, &actx), - SyscallSucceedsWithValue(0)); - - struct epoll_event result[kFDsPerEpoll]; - // Wait while no FDs are ready, but after 200ms fd_adder will add a ready FD - // to epoll which will wake us up. - EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_THAT(pthread_detach(thread), SyscallSucceeds()); - EXPECT_EQ(result[0].data.u64, 0xdeadbeeffacefeed); -} - -TEST(EpollTest, Oneshot) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, - kMagicConstant + i)); - } - - struct epoll_event event; - event.events = EPOLLOUT | EPOLLONESHOT; - event.data.u64 = kMagicConstant; - ASSERT_THAT( - epoll_ctl(epollfd.get(), EPOLL_CTL_MOD, eventfds[0].get(), &event), - SyscallSucceeds()); - - struct epoll_event result[kFDsPerEpoll]; - // One-shot entry means that the first epoll_wait should succeed. - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(result[0].data.u64, kMagicConstant); - - // One-shot entry means that the second epoll_wait should timeout. - EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); -} - -TEST(EpollTest, EdgeTriggered_NoRandomSave) { - // Test edge-triggered entry: make it edge-triggered, first wait should - // return it, second one should time out, make it writable again, third wait - // should return it, fourth wait should timeout. - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfd.get(), - EPOLLOUT | EPOLLET, kMagicConstant)); - - struct epoll_event result[kFDsPerEpoll]; - - { - const DisableSave ds; // May trigger spurious event. - - // Edge-triggered entry means that the first epoll_wait should return the - // event. - ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(result[0].data.u64, kMagicConstant); - - // Edge-triggered entry means that the second epoll_wait should time out. - ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); - } - - uint64_t tmp = ULLONG_MAX - 1; - - // Make an fd non-writable. - ASSERT_THAT(WriteFd(eventfd.get(), &tmp, sizeof(tmp)), - SyscallSucceedsWithValue(sizeof(tmp))); - - // Make the same fd non-writable to trigger a change, which will trigger an - // edge-triggered event. - ASSERT_THAT(ReadFd(eventfd.get(), &tmp, sizeof(tmp)), - SyscallSucceedsWithValue(sizeof(tmp))); - - { - const DisableSave ds; // May trigger spurious event. - - // An edge-triggered event should now be returned. - ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(result[0].data.u64, kMagicConstant); - - // The edge-triggered event had been consumed above, we don't expect to - // get it again. - ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); - } -} - -TEST(EpollTest, OneshotAndEdgeTriggered) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfd.get(), - EPOLLOUT | EPOLLET | EPOLLONESHOT, - kMagicConstant)); - - struct epoll_event result[kFDsPerEpoll]; - // First time one shot edge-triggered entry means that epoll_wait should - // return the event. - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(result[0].data.u64, kMagicConstant); - - // Edge-triggered entry means that the second epoll_wait should time out. - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); - - uint64_t tmp = ULLONG_MAX - 1; - // Make an fd non-writable. - ASSERT_THAT(WriteFd(eventfd.get(), &tmp, sizeof(tmp)), - SyscallSucceedsWithValue(sizeof(tmp))); - // Make the same fd non-writable to trigger a change, which will not trigger - // an edge-triggered event because we've also included EPOLLONESHOT. - ASSERT_THAT(ReadFd(eventfd.get(), &tmp, sizeof(tmp)), - SyscallSucceedsWithValue(sizeof(tmp))); - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); -} - -TEST(EpollTest, CycleOfOneDisallowed) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - - struct epoll_event event; - event.events = EPOLLOUT; - event.data.u64 = kMagicConstant; - - ASSERT_THAT(epoll_ctl(epollfd.get(), EPOLL_CTL_ADD, epollfd.get(), &event), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(EpollTest, CycleOfThreeDisallowed) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - auto epollfd1 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - auto epollfd2 = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - - ASSERT_NO_ERRNO( - RegisterEpollFD(epollfd.get(), epollfd1.get(), EPOLLIN, kMagicConstant)); - ASSERT_NO_ERRNO( - RegisterEpollFD(epollfd1.get(), epollfd2.get(), EPOLLIN, kMagicConstant)); - - struct epoll_event event; - event.events = EPOLLIN; - event.data.u64 = kMagicConstant; - EXPECT_THAT(epoll_ctl(epollfd2.get(), EPOLL_CTL_ADD, epollfd.get(), &event), - SyscallFailsWithErrno(ELOOP)); -} - -TEST(EpollTest, CloseFile) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - ASSERT_NO_ERRNO( - RegisterEpollFD(epollfd.get(), eventfd.get(), EPOLLOUT, kMagicConstant)); - - struct epoll_event result[kFDsPerEpoll]; - ASSERT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, -1), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(result[0].data.u64, kMagicConstant); - - // Close the event fd early. - eventfd.reset(); - - EXPECT_THAT(RetryEINTR(epoll_wait)(epollfd.get(), result, kFDsPerEpoll, 100), - SyscallSucceedsWithValue(0)); -} - -TEST(EpollTest, PipeReaderHupAfterWriterClosed) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - int pipefds[2]; - ASSERT_THAT(pipe(pipefds), SyscallSucceeds()); - FileDescriptor rfd(pipefds[0]); - FileDescriptor wfd(pipefds[1]); - - ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), rfd.get(), 0, kMagicConstant)); - struct epoll_event result[kFDsPerEpoll]; - // Initially, rfd should not generate any events of interest. - ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 0), - SyscallSucceedsWithValue(0)); - // Close the write end of the pipe. - wfd.reset(); - // rfd should now generate EPOLLHUP, which EPOLL_CTL_ADD unconditionally adds - // to the set of events of interest. - ASSERT_THAT(epoll_wait(epollfd.get(), result, kFDsPerEpoll, 0), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(result[0].events, EPOLLHUP); - EXPECT_EQ(result[0].data.u64, kMagicConstant); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc deleted file mode 100644 index dc794415e..000000000 --- a/test/syscalls/linux/eventfd.cc +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/epoll.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/epoll_util.h" -#include "test/util/eventfd_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(EventfdTest, Nonblock) { - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - - uint64_t l; - ASSERT_THAT(read(efd.get(), &l, sizeof(l)), SyscallFailsWithErrno(EAGAIN)); - - l = 1; - ASSERT_THAT(write(efd.get(), &l, sizeof(l)), SyscallSucceeds()); - - l = 0; - ASSERT_THAT(read(efd.get(), &l, sizeof(l)), SyscallSucceeds()); - EXPECT_EQ(l, 1); - - ASSERT_THAT(read(efd.get(), &l, sizeof(l)), SyscallFailsWithErrno(EAGAIN)); -} - -void* read_three_times(void* arg) { - int efd = *reinterpret_cast<int*>(arg); - uint64_t l; - EXPECT_THAT(read(efd, &l, sizeof(l)), SyscallSucceedsWithValue(sizeof(l))); - EXPECT_THAT(read(efd, &l, sizeof(l)), SyscallSucceedsWithValue(sizeof(l))); - EXPECT_THAT(read(efd, &l, sizeof(l)), SyscallSucceedsWithValue(sizeof(l))); - return nullptr; -} - -TEST(EventfdTest, BlockingWrite) { - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_SEMAPHORE)); - int efd = fd.get(); - - pthread_t p; - ASSERT_THAT(pthread_create(&p, nullptr, read_three_times, - reinterpret_cast<void*>(&efd)), - SyscallSucceeds()); - - uint64_t l = 1; - ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); - EXPECT_EQ(l, 1); - - ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); - EXPECT_EQ(l, 1); - - ASSERT_THAT(write(efd, &l, sizeof(l)), SyscallSucceeds()); - EXPECT_EQ(l, 1); - - ASSERT_THAT(pthread_join(p, nullptr), SyscallSucceeds()); -} - -TEST(EventfdTest, SmallWrite) { - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - - uint64_t l = 16; - ASSERT_THAT(write(efd.get(), &l, 4), SyscallFailsWithErrno(EINVAL)); -} - -TEST(EventfdTest, SmallRead) { - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - - uint64_t l = 1; - ASSERT_THAT(write(efd.get(), &l, sizeof(l)), SyscallSucceeds()); - - l = 0; - ASSERT_THAT(read(efd.get(), &l, 4), SyscallFailsWithErrno(EINVAL)); -} - -TEST(EventfdTest, IllegalSeek) { - FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - EXPECT_THAT(lseek(efd.get(), 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); -} - -TEST(EventfdTest, IllegalPread) { - FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - int l; - EXPECT_THAT(pread(efd.get(), &l, sizeof(l), 0), - SyscallFailsWithErrno(ESPIPE)); -} - -TEST(EventfdTest, IllegalPwrite) { - FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - EXPECT_THAT(pwrite(efd.get(), "x", 1, 0), SyscallFailsWithErrno(ESPIPE)); -} - -TEST(EventfdTest, BigWrite) { - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - - uint64_t big[16]; - big[0] = 16; - ASSERT_THAT(write(efd.get(), big, sizeof(big)), SyscallSucceeds()); -} - -TEST(EventfdTest, BigRead) { - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - - uint64_t l = 1; - ASSERT_THAT(write(efd.get(), &l, sizeof(l)), SyscallSucceeds()); - - uint64_t big[16]; - ASSERT_THAT(read(efd.get(), big, sizeof(big)), SyscallSucceeds()); - EXPECT_EQ(big[0], 1); -} - -TEST(EventfdTest, BigWriteBigRead) { - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - - uint64_t l[16]; - l[0] = 16; - ASSERT_THAT(write(efd.get(), l, sizeof(l)), SyscallSucceeds()); - ASSERT_THAT(read(efd.get(), l, sizeof(l)), SyscallSucceeds()); - 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. - constexpr int kEpollTimeoutMs = 10000; - // Create an eventfd descriptor. - FileDescriptor efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(7, EFD_NONBLOCK | EFD_SEMAPHORE)); - // Create an epoll fd to listen to efd. - FileDescriptor epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - // Add efd to epoll. - ASSERT_NO_ERRNO( - RegisterEpollFD(epollfd.get(), efd.get(), EPOLLIN | EPOLLET, efd.get())); - - // Use epoll to get a value from efd. - struct epoll_event out_ev; - int wait_out = epoll_wait(epollfd.get(), &out_ev, 1, kEpollTimeoutMs); - EXPECT_EQ(wait_out, 1); - EXPECT_EQ(efd.get(), out_ev.data.fd); - uint64_t val = 0; - ASSERT_THAT(read(efd.get(), &val, sizeof(val)), SyscallSucceeds()); - EXPECT_EQ(val, 1); - - // Start a thread that, after this thread blocks on epoll_wait, will write to - // efd. This is racy -- it's possible that this write will happen after - // epoll_wait times out. - ScopedThread t([&efd] { - sleep(5); - uint64_t val = 1; - EXPECT_THAT(write(efd.get(), &val, sizeof(val)), - SyscallSucceedsWithValue(sizeof(val))); - }); - - // epoll_wait should return once the thread writes. - wait_out = epoll_wait(epollfd.get(), &out_ev, 1, kEpollTimeoutMs); - EXPECT_EQ(wait_out, 1); - EXPECT_EQ(efd.get(), out_ev.data.fd); - - val = 0; - ASSERT_THAT(read(efd.get(), &val, sizeof(val)), SyscallSucceeds()); - EXPECT_EQ(val, 1); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/exceptions.cc b/test/syscalls/linux/exceptions.cc deleted file mode 100644 index 11dc1c651..000000000 --- a/test/syscalls/linux/exceptions.cc +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> - -#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" - -namespace gvisor { -namespace testing { - -#if defined(__x86_64__) -// Default value for the x87 FPU control word. See Intel SDM Vol 1, Ch 8.1.5 -// "x87 FPU Control Word". -constexpr uint16_t kX87ControlWordDefault = 0x37f; - -// Mask for the divide-by-zero exception. -constexpr uint16_t kX87ControlWordDiv0Mask = 1 << 2; - -// Default value for the SSE control register (MXCSR). See Intel SDM Vol 1, Ch -// 11.6.4 "Initialization of SSE/SSE3 Extensions". -constexpr uint32_t kMXCSRDefault = 0x1f80; - -// Mask for the divide-by-zero exception. -constexpr uint32_t kMXCSRDiv0Mask = 1 << 9; - -// Flag for a pending divide-by-zero exception. -constexpr uint32_t kMXCSRDiv0Flag = 1 << 2; - -void inline Halt() { asm("hlt\r\n"); } - -void inline SetAlignmentCheck() { - asm("subq $128, %%rsp\r\n" // Avoid potential red zone clobber - "pushf\r\n" - "pop %%rax\r\n" - "or $0x40000, %%rax\r\n" - "push %%rax\r\n" - "popf\r\n" - "addq $128, %%rsp\r\n" - : - : - : "ax"); -} - -void inline ClearAlignmentCheck() { - asm("subq $128, %%rsp\r\n" // Avoid potential red zone clobber - "pushf\r\n" - "pop %%rax\r\n" - "mov $0x40000, %%rbx\r\n" - "not %%rbx\r\n" - "and %%rbx, %%rax\r\n" - "push %%rax\r\n" - "popf\r\n" - "addq $128, %%rsp\r\n" - : - : - : "ax", "bx"); -} - -void inline Int3Normal() { asm(".byte 0xcd, 0x03\r\n"); } - -void inline Int3Compact() { asm(".byte 0xcc\r\n"); } - -void InIOHelper(int width, int value) { - EXPECT_EXIT( - { - switch (width) { - case 1: - asm volatile("inb %%dx, %%al" ::"d"(value) : "%eax"); - break; - case 2: - asm volatile("inw %%dx, %%ax" ::"d"(value) : "%eax"); - break; - case 4: - asm volatile("inl %%dx, %%eax" ::"d"(value) : "%eax"); - break; - default: - FAIL() << "invalid input width, only 1, 2 or 4 is allowed"; - } - }, - ::testing::KilledBySignal(SIGSEGV), ""); -} -#elif defined(__aarch64__) -void inline Halt() { asm("hlt #0\r\n"); } -#endif - -TEST(ExceptionTest, Halt) { - // In order to prevent the regular handler from messing with things (and - // perhaps refaulting until some other signal occurs), we reset the handler to - // the default action here and ensure that it dies correctly. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); - -#if defined(__x86_64__) - EXPECT_EXIT(Halt(), ::testing::KilledBySignal(SIGSEGV), ""); -#elif defined(__aarch64__) - EXPECT_EXIT(Halt(), ::testing::KilledBySignal(SIGILL), ""); -#endif -} - -#if defined(__x86_64__) -TEST(ExceptionTest, DivideByZero) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); - - EXPECT_EXIT( - { - uint32_t remainder; - uint32_t quotient; - uint32_t divisor = 0; - uint64_t value = 1; - asm("divl 0(%2)\r\n" - : "=d"(remainder), "=a"(quotient) - : "r"(&divisor), "d"(value >> 32), "a"(value)); - TEST_CHECK(quotient > 0); // Force dependency. - }, - ::testing::KilledBySignal(SIGFPE), ""); -} - -// By default, x87 exceptions are masked and simply return a default value. -TEST(ExceptionTest, X87DivideByZeroMasked) { - int32_t quotient; - int32_t value = 1; - int32_t divisor = 0; - asm("fildl %[value]\r\n" - "fidivl %[divisor]\r\n" - "fistpl %[quotient]\r\n" - : [ quotient ] "=m"(quotient) - : [ value ] "m"(value), [ divisor ] "m"(divisor)); - - EXPECT_EQ(quotient, INT32_MIN); -} - -// When unmasked, division by zero raises SIGFPE. -TEST(ExceptionTest, X87DivideByZeroUnmasked) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); - - EXPECT_EXIT( - { - // Clear the divide by zero exception mask. - constexpr uint16_t kControlWord = - kX87ControlWordDefault & ~kX87ControlWordDiv0Mask; - - int32_t quotient; - int32_t value = 1; - int32_t divisor = 0; - asm volatile( - "fldcw %[cw]\r\n" - "fildl %[value]\r\n" - "fidivl %[divisor]\r\n" - "fistpl %[quotient]\r\n" - : [ quotient ] "=m"(quotient) - : [ cw ] "m"(kControlWord), [ value ] "m"(value), - [ divisor ] "m"(divisor)); - }, - ::testing::KilledBySignal(SIGFPE), ""); -} - -// Pending exceptions in the x87 status register are not clobbered by syscalls. -TEST(ExceptionTest, X87StatusClobber) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); - - EXPECT_EXIT( - { - // Clear the divide by zero exception mask. - constexpr uint16_t kControlWord = - kX87ControlWordDefault & ~kX87ControlWordDiv0Mask; - - int32_t quotient; - int32_t value = 1; - int32_t divisor = 0; - asm volatile( - "fildl %[value]\r\n" - "fidivl %[divisor]\r\n" - // Exception is masked, so it does not occur here. - "fistpl %[quotient]\r\n" - - // SYS_getpid placed in rax by constraint. - "syscall\r\n" - - // Unmask exception. The syscall didn't clobber the pending - // exception, so now it can be raised. - // - // N.B. "a floating-point exception will be generated upon execution - // of the *next* floating-point instruction". - "fldcw %[cw]\r\n" - "fwait\r\n" - : [ quotient ] "=m"(quotient) - : [ value ] "m"(value), [ divisor ] "m"(divisor), "a"(SYS_getpid), - [ cw ] "m"(kControlWord) - : "rcx", "r11"); - }, - ::testing::KilledBySignal(SIGFPE), ""); -} - -// By default, SSE exceptions are masked and simply return a default value. -TEST(ExceptionTest, SSEDivideByZeroMasked) { - uint32_t status; - int32_t quotient; - int32_t value = 1; - int32_t divisor = 0; - asm("cvtsi2ssl %[value], %%xmm0\r\n" - "cvtsi2ssl %[divisor], %%xmm1\r\n" - "divss %%xmm1, %%xmm0\r\n" - "cvtss2sil %%xmm0, %[quotient]\r\n" - : [ quotient ] "=r"(quotient), [ status ] "=r"(status) - : [ value ] "r"(value), [ divisor ] "r"(divisor) - : "xmm0", "xmm1"); - - EXPECT_EQ(quotient, INT32_MIN); -} - -// When unmasked, division by zero raises SIGFPE. -TEST(ExceptionTest, SSEDivideByZeroUnmasked) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGFPE, sa)); - - EXPECT_EXIT( - { - // Clear the divide by zero exception mask. - constexpr uint32_t kMXCSR = kMXCSRDefault & ~kMXCSRDiv0Mask; - - int32_t quotient; - int32_t value = 1; - int32_t divisor = 0; - asm volatile( - "ldmxcsr %[mxcsr]\r\n" - "cvtsi2ssl %[value], %%xmm0\r\n" - "cvtsi2ssl %[divisor], %%xmm1\r\n" - "divss %%xmm1, %%xmm0\r\n" - "cvtss2sil %%xmm0, %[quotient]\r\n" - : [ quotient ] "=r"(quotient) - : [ mxcsr ] "m"(kMXCSR), [ value ] "r"(value), - [ divisor ] "r"(divisor) - : "xmm0", "xmm1"); - }, - ::testing::KilledBySignal(SIGFPE), ""); -} - -// Pending exceptions in the SSE status register are not clobbered by syscalls. -TEST(ExceptionTest, SSEStatusClobber) { - uint32_t mxcsr; - int32_t quotient; - int32_t value = 1; - int32_t divisor = 0; - asm("cvtsi2ssl %[value], %%xmm0\r\n" - "cvtsi2ssl %[divisor], %%xmm1\r\n" - "divss %%xmm1, %%xmm0\r\n" - // Exception is masked, so it does not occur here. - "cvtss2sil %%xmm0, %[quotient]\r\n" - - // SYS_getpid placed in rax by constraint. - "syscall\r\n" - - // Intel SDM Vol 1, Ch 10.2.3.1 "SIMD Floating-Point Mask and Flag Bits": - // "If LDMXCSR or FXRSTOR clears a mask bit and sets the corresponding - // exception flag bit, a SIMD floating-point exception will not be - // generated as a result of this change. The unmasked exception will be - // generated only upon the execution of the next SSE/SSE2/SSE3 instruction - // that detects the unmasked exception condition." - // - // Though ambiguous, empirical evidence indicates that this means that - // exception flags set in the status register will never cause an - // exception to be raised; only a new exception condition will do so. - // - // Thus here we just check for the flag itself rather than trying to raise - // the exception. - "stmxcsr %[mxcsr]\r\n" - : [ quotient ] "=r"(quotient), [ mxcsr ] "+m"(mxcsr) - : [ value ] "r"(value), [ divisor ] "r"(divisor), "a"(SYS_getpid) - : "xmm0", "xmm1", "rcx", "r11"); - - EXPECT_TRUE(mxcsr & kMXCSRDiv0Flag); -} - -TEST(ExceptionTest, IOAccessFault) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); - - InIOHelper(1, 0x0); - InIOHelper(2, 0x7); - InIOHelper(4, 0x6); - InIOHelper(1, 0xffff); - InIOHelper(2, 0xffff); - InIOHelper(4, 0xfffd); -} - -TEST(ExceptionTest, Alignment) { - SetAlignmentCheck(); - ClearAlignmentCheck(); -} - -TEST(ExceptionTest, AlignmentHalt) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); - - // Reported upstream. We need to ensure that bad flags are cleared even in - // fault paths. Set the alignment flag and then generate an exception. - EXPECT_EXIT( - { - SetAlignmentCheck(); - Halt(); - }, - ::testing::KilledBySignal(SIGSEGV), ""); -} - -TEST(ExceptionTest, AlignmentCheck) { - SKIP_IF(PlatformSupportAlignmentCheck() != PlatformSupport::Allowed); - - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGBUS, sa)); - - EXPECT_EXIT( - { - char array[16]; - SetAlignmentCheck(); - for (int i = 0; i < 8; i++) { - // At least 7/8 offsets will be unaligned here. - uint64_t* ptr = reinterpret_cast<uint64_t*>(&array[i]); - asm("mov %0, 0(%0)\r\n" : : "r"(ptr) : "ax"); - } - }, - ::testing::KilledBySignal(SIGBUS), ""); -} - -TEST(ExceptionTest, Int3Normal) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGTRAP, sa)); - - EXPECT_EXIT(Int3Normal(), ::testing::KilledBySignal(SIGTRAP), ""); -} - -TEST(ExceptionTest, Int3Compact) { - // See above. - struct sigaction sa = {}; - sa.sa_handler = SIG_DFL; - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGTRAP, sa)); - - EXPECT_EXIT(Int3Compact(), ::testing::KilledBySignal(SIGTRAP), ""); -} -#endif - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/exec.cc b/test/syscalls/linux/exec.cc deleted file mode 100644 index c5acfc794..000000000 --- a/test/syscalls/linux/exec.cc +++ /dev/null @@ -1,904 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/exec.h" - -#include <errno.h> -#include <fcntl.h> -#include <sys/eventfd.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <unistd.h> - -#include <iostream> -#include <memory> -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/match.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr char kBasicWorkload[] = "test/syscalls/linux/exec_basic_workload"; -constexpr char kExitScript[] = "test/syscalls/linux/exit_script"; -constexpr char kStateWorkload[] = "test/syscalls/linux/exec_state_workload"; -constexpr char kProcExeWorkload[] = - "test/syscalls/linux/exec_proc_exe_workload"; -constexpr char kAssertClosedWorkload[] = - "test/syscalls/linux/exec_assert_closed_workload"; -constexpr char kPriorityWorkload[] = "test/syscalls/linux/priority_execve"; - -constexpr char kExit42[] = "--exec_exit_42"; -constexpr char kExecWithThread[] = "--exec_exec_with_thread"; -constexpr char kExecFromThread[] = "--exec_exec_from_thread"; - -// Runs file specified by dirfd and pathname with argv and checks that the exit -// status is expect_status and that stderr contains expect_stderr. -void CheckExecHelper(const absl::optional<int32_t> dirfd, - const std::string& pathname, const ExecveArray& argv, - const ExecveArray& envv, const int flags, - int expect_status, const std::string& expect_stderr) { - int pipe_fds[2]; - ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds()); - - FileDescriptor read_fd(pipe_fds[0]); - FileDescriptor write_fd(pipe_fds[1]); - - pid_t child; - int execve_errno; - - const auto remap_stderr = [pipe_fds] { - // Remap stdin and stdout to /dev/null. - int fd = open("/dev/null", O_RDWR | O_CLOEXEC); - if (fd < 0) { - _exit(errno); - } - - int ret = dup2(fd, 0); - if (ret < 0) { - _exit(errno); - } - - ret = dup2(fd, 1); - if (ret < 0) { - _exit(errno); - } - - // And stderr to the pipe. - ret = dup2(pipe_fds[1], 2); - if (ret < 0) { - _exit(errno); - } - - // Here, we'd ideally close all other FDs inherited from the parent. - // However, that's not worth the effort and CloexecNormalFile and - // CloexecEventfd depend on that not happening. - }; - - Cleanup kill; - if (dirfd.has_value()) { - kill = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(*dirfd, pathname, argv, - envv, flags, remap_stderr, - &child, &execve_errno)); - } else { - kill = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(pathname, argv, envv, remap_stderr, &child, &execve_errno)); - } - - ASSERT_EQ(0, execve_errno); - - // Not needed anymore. - write_fd.reset(); - - // Read stderr until the child exits. - std::string output; - constexpr int kSize = 128; - char buf[kSize]; - int n; - do { - ASSERT_THAT(n = ReadFd(read_fd.get(), buf, kSize), SyscallSucceeds()); - if (n > 0) { - output.append(buf, n); - } - } while (n > 0); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_EQ(status, expect_status); - - // Process cleanup no longer needed. - kill.Release(); - - EXPECT_TRUE(absl::StrContains(output, expect_stderr)) << output; -} - -void CheckExec(const std::string& filename, const ExecveArray& argv, - const ExecveArray& envv, int expect_status, - const std::string& expect_stderr) { - CheckExecHelper(/*dirfd=*/absl::optional<int32_t>(), filename, argv, envv, - /*flags=*/0, expect_status, expect_stderr); -} - -void CheckExecveat(const int32_t dirfd, const std::string& pathname, - const ExecveArray& argv, const ExecveArray& envv, - const int flags, int expect_status, - const std::string& expect_stderr) { - CheckExecHelper(absl::optional<int32_t>(dirfd), pathname, argv, envv, flags, - expect_status, expect_stderr); -} - -TEST(ExecTest, EmptyPath) { - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec("", {}, {}, nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, ENOENT); -} - -TEST(ExecTest, Basic) { - CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload)}, {}, - ArgEnvExitStatus(0, 0), - absl::StrCat(RunfilePath(kBasicWorkload), "\n")); -} - -TEST(ExecTest, OneArg) { - CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload), "1"}, {}, - ArgEnvExitStatus(1, 0), - absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n")); -} - -TEST(ExecTest, FiveArg) { - CheckExec(RunfilePath(kBasicWorkload), - {RunfilePath(kBasicWorkload), "1", "2", "3", "4", "5"}, {}, - ArgEnvExitStatus(5, 0), - absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n2\n3\n4\n5\n")); -} - -TEST(ExecTest, OneEnv) { - CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload)}, {"1"}, - ArgEnvExitStatus(0, 1), - absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n")); -} - -TEST(ExecTest, FiveEnv) { - CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload)}, - {"1", "2", "3", "4", "5"}, ArgEnvExitStatus(0, 5), - absl::StrCat(RunfilePath(kBasicWorkload), "\n1\n2\n3\n4\n5\n")); -} - -TEST(ExecTest, OneArgOneEnv) { - CheckExec(RunfilePath(kBasicWorkload), {RunfilePath(kBasicWorkload), "arg"}, - {"env"}, ArgEnvExitStatus(1, 1), - absl::StrCat(RunfilePath(kBasicWorkload), "\narg\nenv\n")); -} - -TEST(ExecTest, InterpreterScript) { - CheckExec(RunfilePath(kExitScript), {RunfilePath(kExitScript), "25"}, {}, - ArgEnvExitStatus(25, 0), ""); -} - -// Everything after the path in the interpreter script is a single argument. -TEST(ExecTest, InterpreterScriptArgSplit) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo bar"), - 0755)); - - CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), - absl::StrCat(link.path(), "\nfoo bar\n", script.path(), "\n")); -} - -// Original argv[0] is replaced with the script path. -TEST(ExecTest, InterpreterScriptArgvZero) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); - - CheckExec(script.path(), {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), - absl::StrCat(link.path(), "\n", script.path(), "\n")); -} - -// Original argv[0] is replaced with the script path, exactly as passed to -// execve. -TEST(ExecTest, InterpreterScriptArgvZeroRelative) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); - - auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); - auto script_relative = - ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); - - CheckExec(script_relative, {"REPLACED"}, {}, ArgEnvExitStatus(1, 0), - absl::StrCat(link.path(), "\n", script_relative, "\n")); -} - -// argv[0] is added as the script path, even if there was none. -TEST(ExecTest, InterpreterScriptArgvZeroAdded) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path()), 0755)); - - CheckExec(script.path(), {}, {}, ArgEnvExitStatus(1, 0), - absl::StrCat(link.path(), "\n", script.path(), "\n")); -} - -// A NUL byte in the script line ends parsing. -TEST(ExecTest, InterpreterScriptArgNUL) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), - absl::StrCat("#!", link.path(), " foo", std::string(1, '\0'), "bar"), - 0755)); - - CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), - absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); -} - -// Trailing whitespace following interpreter path is ignored. -TEST(ExecTest, InterpreterScriptTrailingWhitespace) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " "), 0755)); - - CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(1, 0), - absl::StrCat(link.path(), "\n", script.path(), "\n")); -} - -// Multiple whitespace characters between interpreter and arg allowed. -TEST(ExecTest, InterpreterScriptArgWhitespace) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " foo"), 0755)); - - CheckExec(script.path(), {script.path()}, {}, ArgEnvExitStatus(2, 0), - absl::StrCat(link.path(), "\nfoo\n", script.path(), "\n")); -} - -TEST(ExecTest, InterpreterScriptNoPath) { - TempPath script = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!", 0755)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, ENOEXEC); -} - -// AT_EXECFN is the path passed to execve. -TEST(ExecTest, ExecFn) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kStateWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link.path(), " PrintExecFn"), - 0755)); - - // Pass the script as a relative path and assert that is what appears in - // AT_EXECFN. - auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); - auto script_relative = - ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, script.path())); - - CheckExec(script_relative, {script_relative}, {}, ArgEnvExitStatus(0, 0), - absl::StrCat(script_relative, "\n")); -} - -TEST(ExecTest, ExecName) { - std::string path = RunfilePath(kStateWorkload); - - CheckExec(path, {path, "PrintExecName"}, {}, ArgEnvExitStatus(0, 0), - absl::StrCat(Basename(path).substr(0, 15), "\n")); -} - -TEST(ExecTest, ExecNameScript) { - // Symlink through /tmp to ensure the path is short enough. - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kStateWorkload))); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), - absl::StrCat("#!", link.path(), " PrintExecName"), 0755)); - - std::string script_path = script.path(); - - CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), - absl::StrCat(Basename(script_path).substr(0, 15), "\n")); -} - -// execve may be called by a multithreaded process. -TEST(ExecTest, WithSiblingThread) { - CheckExec("/proc/self/exe", {"/proc/self/exe", kExecWithThread}, {}, - W_EXITCODE(42, 0), ""); -} - -// execve may be called from a thread other than the leader of a multithreaded -// process. -TEST(ExecTest, FromSiblingThread) { - CheckExec("/proc/self/exe", {"/proc/self/exe", kExecFromThread}, {}, - W_EXITCODE(42, 0), ""); -} - -TEST(ExecTest, NotFound) { - char* const argv[] = {nullptr}; - char* const envp[] = {nullptr}; - EXPECT_THAT(execve("/file/does/not/exist", argv, envp), - SyscallFailsWithErrno(ENOENT)); -} - -TEST(ExecTest, NoExecPerm) { - char* const argv[] = {nullptr}; - char* const envp[] = {nullptr}; - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - EXPECT_THAT(execve(f.path().c_str(), argv, envp), - SyscallFailsWithErrno(EACCES)); -} - -// A signal handler we never expect to be called. -void SignalHandler(int signo) { - std::cerr << "Signal " << signo << " raised." << std::endl; - exit(1); -} - -// Signal handlers are reset on execve(2), unless they have default or ignored -// disposition. -TEST(ExecStateTest, HandlerReset) { - struct sigaction sa; - sa.sa_handler = SignalHandler; - ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); - - ExecveArray args = { - RunfilePath(kStateWorkload), - "CheckSigHandler", - absl::StrCat(SIGUSR1), - absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_DFL))), - }; - - CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); -} - -// Ignored signal dispositions are not reset. -TEST(ExecStateTest, IgnorePreserved) { - struct sigaction sa; - sa.sa_handler = SIG_IGN; - ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); - - ExecveArray args = { - RunfilePath(kStateWorkload), - "CheckSigHandler", - absl::StrCat(SIGUSR1), - absl::StrCat(absl::Hex(reinterpret_cast<uintptr_t>(SIG_IGN))), - }; - - CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); -} - -// Signal masks are not reset on exec -TEST(ExecStateTest, SignalMask) { - sigset_t s; - sigemptyset(&s); - sigaddset(&s, SIGUSR1); - ASSERT_THAT(sigprocmask(SIG_BLOCK, &s, nullptr), SyscallSucceeds()); - - ExecveArray args = { - RunfilePath(kStateWorkload), - "CheckSigBlocked", - absl::StrCat(SIGUSR1), - }; - - CheckExec(RunfilePath(kStateWorkload), args, {}, W_EXITCODE(0, 0), ""); -} - -// itimers persist across execve. -// N.B. Timers created with timer_create(2) should not be preserved! -TEST(ExecStateTest, ItimerPreserved) { - // The fork in ForkAndExec clears itimers, so only set them up after fork. - auto setup_itimer = [] { - // Ignore SIGALRM, as we don't actually care about timer - // expirations. - struct sigaction sa; - sa.sa_handler = SIG_IGN; - int ret = sigaction(SIGALRM, &sa, nullptr); - if (ret < 0) { - _exit(errno); - } - - struct itimerval itv; - itv.it_interval.tv_sec = 1; - itv.it_interval.tv_usec = 0; - itv.it_value.tv_sec = 1; - itv.it_value.tv_usec = 0; - ret = setitimer(ITIMER_REAL, &itv, nullptr); - if (ret < 0) { - _exit(errno); - } - }; - - std::string filename = RunfilePath(kStateWorkload); - ExecveArray argv = { - filename, - "CheckItimerEnabled", - absl::StrCat(ITIMER_REAL), - }; - - pid_t child; - int execve_errno; - auto kill = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(filename, argv, {}, setup_itimer, &child, &execve_errno)); - ASSERT_EQ(0, execve_errno); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_EQ(0, status); - - // Process cleanup no longer needed. - kill.Release(); -} - -TEST(ProcSelfExe, ChangesAcrossExecve) { - // See exec_proc_exe_workload for more details. We simply - // assert that the /proc/self/exe link changes across execve. - CheckExec(RunfilePath(kProcExeWorkload), - {RunfilePath(kProcExeWorkload), - ASSERT_NO_ERRNO_AND_VALUE(ProcessExePath(getpid()))}, - {}, W_EXITCODE(0, 0), ""); -} - -TEST(ExecTest, CloexecNormalFile) { - TempPath tempFile = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "bar", 0755)); - const FileDescriptor fd_closed_on_exec = - ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC)); - - CheckExec(RunfilePath(kAssertClosedWorkload), - {RunfilePath(kAssertClosedWorkload), - absl::StrCat(fd_closed_on_exec.get())}, - {}, W_EXITCODE(0, 0), ""); - - // The assert closed workload exits with code 2 if the file still exists. We - // can use this to do a negative test. - const FileDescriptor fd_open_on_exec = - ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY)); - - CheckExec( - RunfilePath(kAssertClosedWorkload), - {RunfilePath(kAssertClosedWorkload), absl::StrCat(fd_open_on_exec.get())}, - {}, W_EXITCODE(2, 0), ""); -} - -TEST(ExecTest, CloexecEventfd) { - int efd; - ASSERT_THAT(efd = eventfd(0, EFD_CLOEXEC), SyscallSucceeds()); - FileDescriptor fd(efd); - - CheckExec(RunfilePath(kAssertClosedWorkload), - {RunfilePath(kAssertClosedWorkload), absl::StrCat(fd.get())}, {}, - W_EXITCODE(0, 0), ""); -} - -constexpr int kLinuxMaxSymlinks = 40; - -TEST(ExecTest, SymlinkLimitExceeded) { - std::string path = RunfilePath(kBasicWorkload); - - // Hold onto TempPath objects so they are not destructed prematurely. - std::vector<TempPath> symlinks; - for (int i = 0; i < kLinuxMaxSymlinks + 1; i++) { - symlinks.push_back( - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo("/tmp", path))); - path = symlinks[i].path(); - } - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(path, {path}, {}, /*child=*/nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, ELOOP); -} - -TEST(ExecTest, SymlinkLimitRefreshedForInterpreter) { - std::string tmp_dir = "/tmp"; - std::string interpreter_path = "/bin/echo"; - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - tmp_dir, absl::StrCat("#!", interpreter_path), 0755)); - std::string script_path = script.path(); - - // Hold onto TempPath objects so they are not destructed prematurely. - std::vector<TempPath> interpreter_symlinks; - std::vector<TempPath> script_symlinks; - // Replace both the interpreter and script paths with symlink chains of just - // over half the symlink limit each; this is the minimum required to test that - // the symlink limit applies separately to each traversal, while tolerating - // some symlinks in the resolution of (the original) interpreter_path and - // script_path. - for (int i = 0; i < (kLinuxMaxSymlinks / 2) + 1; i++) { - interpreter_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(tmp_dir, interpreter_path))); - interpreter_path = interpreter_symlinks[i].path(); - script_symlinks.push_back(ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(tmp_dir, script_path))); - script_path = script_symlinks[i].path(); - } - - CheckExec(script_path, {script_path}, {}, ArgEnvExitStatus(0, 0), ""); -} - -TEST(ExecveatTest, BasicWithFDCWD) { - std::string path = RunfilePath(kBasicWorkload); - CheckExecveat(AT_FDCWD, path, {path}, {}, /*flags=*/0, ArgEnvExitStatus(0, 0), - absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, Basic) { - std::string absolute_path = RunfilePath(kBasicWorkload); - std::string parent_dir = std::string(Dirname(absolute_path)); - std::string base = std::string(Basename(absolute_path)); - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); - - CheckExecveat(dirfd.get(), base, {absolute_path}, {}, /*flags=*/0, - ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n")); -} - -TEST(ExecveatTest, FDNotADirectory) { - std::string absolute_path = RunfilePath(kBasicWorkload); - std::string base = std::string(Basename(absolute_path)); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(absolute_path, 0)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), base, {absolute_path}, {}, - /*flags=*/0, /*child=*/nullptr, - &execve_errno)); - EXPECT_EQ(execve_errno, ENOTDIR); -} - -TEST(ExecveatTest, AbsolutePathWithFDCWD) { - std::string path = RunfilePath(kBasicWorkload); - CheckExecveat(AT_FDCWD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0, - absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, AbsolutePath) { - std::string path = RunfilePath(kBasicWorkload); - // File descriptor should be ignored when an absolute path is given. - const int32_t badFD = -1; - CheckExecveat(badFD, path, {path}, {}, ArgEnvExitStatus(0, 0), 0, - absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, EmptyPathBasic) { - std::string path = RunfilePath(kBasicWorkload); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); - - CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH, ArgEnvExitStatus(0, 0), - absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, EmptyPathWithDirFD) { - std::string path = RunfilePath(kBasicWorkload); - std::string parent_dir = std::string(Dirname(path)); - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), "", {path}, {}, - AT_EMPTY_PATH, - /*child=*/nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, EACCES); -} - -TEST(ExecveatTest, EmptyPathWithoutEmptyPathFlag) { - std::string path = RunfilePath(kBasicWorkload); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat( - fd.get(), "", {path}, {}, /*flags=*/0, /*child=*/nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, ENOENT); -} - -TEST(ExecveatTest, AbsolutePathWithEmptyPathFlag) { - std::string path = RunfilePath(kBasicWorkload); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_PATH)); - - CheckExecveat(fd.get(), path, {path}, {}, AT_EMPTY_PATH, - ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, RelativePathWithEmptyPathFlag) { - std::string absolute_path = RunfilePath(kBasicWorkload); - std::string parent_dir = std::string(Dirname(absolute_path)); - std::string base = std::string(Basename(absolute_path)); - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); - - CheckExecveat(dirfd.get(), base, {absolute_path}, {}, AT_EMPTY_PATH, - ArgEnvExitStatus(0, 0), absl::StrCat(absolute_path, "\n")); -} - -TEST(ExecveatTest, SymlinkNoFollowWithRelativePath) { - std::string parent_dir = "/tmp"; - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(parent_dir, RunfilePath(kBasicWorkload))); - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_DIRECTORY)); - std::string base = std::string(Basename(link.path())); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), base, {base}, {}, - AT_SYMLINK_NOFOLLOW, - /*child=*/nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, ELOOP); -} - -TEST(ExecveatTest, UnshareFiles) { - TempPath tempFile = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "bar", 0755)); - const FileDescriptor fd_closed_on_exec = - ASSERT_NO_ERRNO_AND_VALUE(Open(tempFile.path(), O_RDONLY | O_CLOEXEC)); - - ExecveArray argv = {"test"}; - ExecveArray envp; - std::string child_path = RunfilePath(kBasicWorkload); - pid_t child = - syscall(__NR_clone, SIGCHLD | CLONE_VFORK | CLONE_FILES, 0, 0, 0, 0); - if (child == 0) { - execve(child_path.c_str(), argv.get(), envp.get()); - _exit(1); - } - ASSERT_THAT(child, SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_EQ(status, 0); - - struct stat st; - EXPECT_THAT(fstat(fd_closed_on_exec.get(), &st), SyscallSucceeds()); -} - -TEST(ExecveatTest, SymlinkNoFollowWithAbsolutePath) { - std::string parent_dir = "/tmp"; - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(parent_dir, RunfilePath(kBasicWorkload))); - std::string path = link.path(); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(AT_FDCWD, path, {path}, {}, - AT_SYMLINK_NOFOLLOW, - /*child=*/nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, ELOOP); -} - -TEST(ExecveatTest, SymlinkNoFollowAndEmptyPath) { - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo("/tmp", RunfilePath(kBasicWorkload))); - std::string path = link.path(); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, 0)); - - CheckExecveat(fd.get(), "", {path}, {}, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, - ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, SymlinkNoFollowIgnoreSymlinkAncestor) { - TempPath parent_link = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateSymlinkTo("/tmp", "/bin")); - std::string path_with_symlink = JoinPath(parent_link.path(), "echo"); - - CheckExecveat(AT_FDCWD, path_with_symlink, {path_with_symlink}, {}, - AT_SYMLINK_NOFOLLOW, ArgEnvExitStatus(0, 0), ""); -} - -TEST(ExecveatTest, SymlinkNoFollowWithNormalFile) { - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/bin", O_DIRECTORY)); - - CheckExecveat(dirfd.get(), "echo", {"echo"}, {}, AT_SYMLINK_NOFOLLOW, - ArgEnvExitStatus(0, 0), ""); -} - -TEST(ExecveatTest, BasicWithCloexecFD) { - std::string path = RunfilePath(kBasicWorkload); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CLOEXEC)); - - CheckExecveat(fd.get(), "", {path}, {}, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH, - ArgEnvExitStatus(0, 0), absl::StrCat(path, "\n")); -} - -TEST(ExecveatTest, InterpreterScriptWithCloexecFD) { - std::string path = RunfilePath(kExitScript); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CLOEXEC)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(fd.get(), "", {path}, {}, - AT_EMPTY_PATH, /*child=*/nullptr, - &execve_errno)); - EXPECT_EQ(execve_errno, ENOENT); -} - -TEST(ExecveatTest, InterpreterScriptWithCloexecDirFD) { - std::string absolute_path = RunfilePath(kExitScript); - std::string parent_dir = std::string(Dirname(absolute_path)); - std::string base = std::string(Basename(absolute_path)); - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent_dir, O_CLOEXEC | O_DIRECTORY)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat(dirfd.get(), base, {base}, {}, - /*flags=*/0, /*child=*/nullptr, - &execve_errno)); - EXPECT_EQ(execve_errno, ENOENT); -} - -TEST(ExecveatTest, InvalidFlags) { - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE(ForkAndExecveat( - /*dirfd=*/-1, "", {}, {}, /*flags=*/0xFFFF, /*child=*/nullptr, - &execve_errno)); - EXPECT_EQ(execve_errno, EINVAL); -} - -// Priority consistent across calls to execve() -TEST(GetpriorityTest, ExecveMaintainsPriority) { - int prio = 16; - ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), prio), SyscallSucceeds()); - - // To avoid trying to use negative exit values, check for - // 20 - prio. Since prio should always be in the range [-20, 19], - // this leave expected_exit_code in the range [1, 40]. - int expected_exit_code = 20 - prio; - - // Program run (priority_execve) will exit(X) where - // X=getpriority(PRIO_PROCESS,0). Check that this exit value is prio. - CheckExec(RunfilePath(kPriorityWorkload), {RunfilePath(kPriorityWorkload)}, - {}, W_EXITCODE(expected_exit_code, 0), ""); -} - -void ExecWithThread() { - // Used to ensure that the thread has actually started. - absl::Mutex mu; - bool started = false; - - ScopedThread t([&] { - mu.Lock(); - started = true; - mu.Unlock(); - - while (true) { - pause(); - } - }); - - mu.LockWhen(absl::Condition(&started)); - mu.Unlock(); - - const ExecveArray argv = {"/proc/self/exe", kExit42}; - const ExecveArray envv; - - execve("/proc/self/exe", argv.get(), envv.get()); - exit(errno); -} - -void ExecFromThread() { - ScopedThread t([] { - const ExecveArray argv = {"/proc/self/exe", kExit42}; - const ExecveArray envv; - - execve("/proc/self/exe", argv.get(), envv.get()); - exit(errno); - }); - - while (true) { - pause(); - } -} - -bool ValidateProcCmdlineVsArgv(const int argc, const char* const* argv) { - auto contents_or = GetContents("/proc/self/cmdline"); - if (!contents_or.ok()) { - std::cerr << "Unable to get /proc/self/cmdline: " << contents_or.error() - << std::endl; - return false; - } - auto contents = contents_or.ValueOrDie(); - if (contents.back() != '\0') { - std::cerr << "Non-null terminated /proc/self/cmdline!" << std::endl; - return false; - } - contents.pop_back(); - std::vector<std::string> procfs_cmdline = absl::StrSplit(contents, '\0'); - - if (static_cast<int>(procfs_cmdline.size()) != argc) { - std::cerr << "argc = " << argc << " != " << procfs_cmdline.size() - << std::endl; - return false; - } - - for (int i = 0; i < argc; ++i) { - if (procfs_cmdline[i] != argv[i]) { - std::cerr << "Procfs command line argument " << i << " mismatch " - << procfs_cmdline[i] << " != " << argv[i] << std::endl; - return false; - } - } - return true; -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - // Start by validating that the stack argv is consistent with procfs. - if (!gvisor::testing::ValidateProcCmdlineVsArgv(argc, argv)) { - return 1; - } - - // Some of these tests require no background threads, so check for them before - // TestInit. - for (int i = 0; i < argc; i++) { - absl::string_view arg(argv[i]); - - if (arg == gvisor::testing::kExit42) { - return 42; - } - if (arg == gvisor::testing::kExecWithThread) { - gvisor::testing::ExecWithThread(); - return 1; - } - if (arg == gvisor::testing::kExecFromThread) { - gvisor::testing::ExecFromThread(); - return 1; - } - } - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/exec.h b/test/syscalls/linux/exec.h deleted file mode 100644 index 5c0f7e654..000000000 --- a/test/syscalls/linux/exec.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_EXEC_H_ -#define GVISOR_TEST_SYSCALLS_EXEC_H_ - -#include <sys/wait.h> - -namespace gvisor { -namespace testing { - -// Returns the exit code used by exec_basic_workload. -inline int ArgEnvExitCode(int args, int envs) { return args + envs * 10; } - -// Returns the exit status used by exec_basic_workload. -inline int ArgEnvExitStatus(int args, int envs) { - return W_EXITCODE(ArgEnvExitCode(args, envs), 0); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_EXEC_H_ diff --git a/test/syscalls/linux/exec_assert_closed_workload.cc b/test/syscalls/linux/exec_assert_closed_workload.cc deleted file mode 100644 index 95643618d..000000000 --- a/test/syscalls/linux/exec_assert_closed_workload.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <iostream> - -#include "absl/strings/numbers.h" - -int main(int argc, char** argv) { - if (argc != 2) { - std::cerr << "need two arguments, got " << argc; - exit(1); - } - int fd; - if (!absl::SimpleAtoi(argv[1], &fd)) { - std::cerr << "fd: " << argv[1] << " could not be parsed" << std::endl; - exit(1); - } - struct stat s; - if (fstat(fd, &s) == 0) { - std::cerr << "fd: " << argv[1] << " should not be valid" << std::endl; - exit(2); - } - if (errno != EBADF) { - std::cerr << "fstat fd: " << argv[1] << " got errno: " << errno - << " wanted: " << EBADF << std::endl; - exit(1); - } - return 0; -} diff --git a/test/syscalls/linux/exec_basic_workload.cc b/test/syscalls/linux/exec_basic_workload.cc deleted file mode 100644 index 1bbd6437e..000000000 --- a/test/syscalls/linux/exec_basic_workload.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdlib.h> - -#include <iostream> - -#include "test/syscalls/linux/exec.h" - -int main(int argc, char** argv, char** envp) { - int i; - for (i = 0; i < argc; i++) { - std::cerr << argv[i] << std::endl; - } - for (i = 0; envp[i] != nullptr; i++) { - std::cerr << envp[i] << std::endl; - } - exit(gvisor::testing::ArgEnvExitCode(argc - 1, i)); - return 0; -} diff --git a/test/syscalls/linux/exec_binary.cc b/test/syscalls/linux/exec_binary.cc deleted file mode 100644 index b0fb120c6..000000000 --- a/test/syscalls/linux/exec_binary.cc +++ /dev/null @@ -1,1681 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <elf.h> -#include <errno.h> -#include <signal.h> -#include <sys/ptrace.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/user.h> -#include <unistd.h> - -#include <algorithm> -#include <functional> -#include <iterator> -#include <tuple> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/proc_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using ::testing::AnyOf; -using ::testing::Eq; - -#if !defined(__x86_64__) && !defined(__aarch64__) -// The assembly stub and ELF internal details must be ported to other arches. -#error "Test only supported on x86-64/arm64" -#endif // __x86_64__ || __aarch64__ - -#if defined(__x86_64__) -#define EM_TYPE EM_X86_64 -#define IP_REG(p) ((p).rip) -#define RAX_REG(p) ((p).rax) -#define RDI_REG(p) ((p).rdi) -#define RETURN_REG(p) ((p).rax) - -// amd64 stub that calls PTRACE_TRACEME and sends itself SIGSTOP. -const char kPtraceCode[] = { - // movq $101, %rax /* ptrace */ - '\x48', - '\xc7', - '\xc0', - '\x65', - '\x00', - '\x00', - '\x00', - // movq $0, %rsi /* PTRACE_TRACEME */ - '\x48', - '\xc7', - '\xc6', - '\x00', - '\x00', - '\x00', - '\x00', - // movq $0, %rdi - '\x48', - '\xc7', - '\xc7', - '\x00', - '\x00', - '\x00', - '\x00', - // movq $0, %rdx - '\x48', - '\xc7', - '\xc2', - '\x00', - '\x00', - '\x00', - '\x00', - // movq $0, %r10 - '\x49', - '\xc7', - '\xc2', - '\x00', - '\x00', - '\x00', - '\x00', - // syscall - '\x0f', - '\x05', - - // movq $39, %rax /* getpid */ - '\x48', - '\xc7', - '\xc0', - '\x27', - '\x00', - '\x00', - '\x00', - // syscall - '\x0f', - '\x05', - - // movq %rax, %rdi /* pid */ - '\x48', - '\x89', - '\xc7', - // movq $62, %rax /* kill */ - '\x48', - '\xc7', - '\xc0', - '\x3e', - '\x00', - '\x00', - '\x00', - // movq $19, %rsi /* SIGSTOP */ - '\x48', - '\xc7', - '\xc6', - '\x13', - '\x00', - '\x00', - '\x00', - // syscall - '\x0f', - '\x05', -}; - -// Size of a syscall instruction. -constexpr int kSyscallSize = 2; - -#elif defined(__aarch64__) -#define EM_TYPE EM_AARCH64 -#define IP_REG(p) ((p).pc) -#define RAX_REG(p) ((p).regs[8]) -#define RDI_REG(p) ((p).regs[0]) -#define RETURN_REG(p) ((p).regs[0]) - -const char kPtraceCode[] = { - // MOVD $117, R8 /* ptrace */ - '\xa8', - '\x0e', - '\x80', - '\xd2', - // MOVD $0, R0 /* PTRACE_TRACEME */ - '\x00', - '\x00', - '\x80', - '\xd2', - // MOVD $0, R1 /* pid */ - '\x01', - '\x00', - '\x80', - '\xd2', - // MOVD $0, R2 /* addr */ - '\x02', - '\x00', - '\x80', - '\xd2', - // MOVD $0, R3 /* data */ - '\x03', - '\x00', - '\x80', - '\xd2', - // SVC - '\x01', - '\x00', - '\x00', - '\xd4', - // MOVD $172, R8 /* getpid */ - '\x88', - '\x15', - '\x80', - '\xd2', - // SVC - '\x01', - '\x00', - '\x00', - '\xd4', - // MOVD $129, R8 /* kill, R0=pid */ - '\x28', - '\x10', - '\x80', - '\xd2', - // MOVD $19, R1 /* SIGSTOP */ - '\x61', - '\x02', - '\x80', - '\xd2', - // SVC - '\x01', - '\x00', - '\x00', - '\xd4', -}; -// Size of a syscall instruction. -constexpr int kSyscallSize = 4; -#else -#error "Unknown architecture" -#endif - -// This test suite tests executable loading in the kernel (ELF and interpreter -// scripts). - -// Parameterized ELF types for 64 and 32 bit. -template <int Size> -struct ElfTypes; - -template <> -struct ElfTypes<64> { - typedef Elf64_Ehdr ElfEhdr; - typedef Elf64_Phdr ElfPhdr; -}; - -template <> -struct ElfTypes<32> { - typedef Elf32_Ehdr ElfEhdr; - typedef Elf32_Phdr ElfPhdr; -}; - -template <int Size> -struct ElfBinary { - using ElfEhdr = typename ElfTypes<Size>::ElfEhdr; - using ElfPhdr = typename ElfTypes<Size>::ElfPhdr; - - ElfEhdr header = {}; - std::vector<ElfPhdr> phdrs; - std::vector<char> data; - - // UpdateOffsets updates p_offset, p_vaddr in all phdrs to account for the - // space taken by the header and phdrs. - // - // It also updates header.e_phnum and adds the offset to header.e_entry to - // account for the headers residing in the first PT_LOAD segment. - // - // Before calling UpdateOffsets each of those fields should be the appropriate - // offset into data. - void UpdateOffsets() { - size_t offset = sizeof(header) + phdrs.size() * sizeof(ElfPhdr); - header.e_entry += offset; - header.e_phnum = phdrs.size(); - for (auto& p : phdrs) { - p.p_offset += offset; - p.p_vaddr += offset; - } - } - - // AddInterpreter adds a PT_INTERP segment with the passed contents. - // - // A later call to UpdateOffsets is required to make the new phdr valid. - void AddInterpreter(std::vector<char> contents) { - const int start = data.size(); - data.insert(data.end(), contents.begin(), contents.end()); - const int size = data.size() - start; - - ElfPhdr phdr = {}; - phdr.p_type = PT_INTERP; - phdr.p_offset = start; - phdr.p_filesz = size; - phdr.p_memsz = size; - // "If [PT_INTERP] is present, it must precede any loadable segment entry." - phdrs.insert(phdrs.begin(), phdr); - } - - // Writes the header, phdrs, and data to fd. - PosixError Write(int fd) const { - int ret = WriteFd(fd, &header, sizeof(header)); - if (ret < 0) { - return PosixError(errno, "failed to write header"); - } else if (ret != sizeof(header)) { - return PosixError(EIO, absl::StrCat("short write of header: ", ret)); - } - - for (auto const& p : phdrs) { - ret = WriteFd(fd, &p, sizeof(p)); - if (ret < 0) { - return PosixError(errno, "failed to write phdr"); - } else if (ret != sizeof(p)) { - return PosixError(EIO, absl::StrCat("short write of phdr: ", ret)); - } - } - - ret = WriteFd(fd, data.data(), data.size()); - if (ret < 0) { - return PosixError(errno, "failed to write data"); - } else if (ret != static_cast<int>(data.size())) { - return PosixError(EIO, absl::StrCat("short write of data: ", ret)); - } - - return NoError(); - } -}; - -// Creates a new temporary executable ELF file in parent with elf as the -// contents. -template <int Size> -PosixErrorOr<TempPath> CreateElfWith(absl::string_view parent, - ElfBinary<Size> const& elf) { - ASSIGN_OR_RETURN_ERRNO( - auto file, TempPath::CreateFileWith(parent, absl::string_view(), 0755)); - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(file.path(), O_RDWR)); - RETURN_IF_ERRNO(elf.Write(fd.get())); - return std::move(file); -} - -// Creates a new temporary executable ELF file with elf as the contents. -template <int Size> -PosixErrorOr<TempPath> CreateElfWith(ElfBinary<Size> const& elf) { - return CreateElfWith(GetAbsoluteTestTmpdir(), elf); -} - -// Wait for pid to stop, and assert that it stopped via SIGSTOP. -PosixError WaitStopped(pid_t pid) { - int status; - int ret = RetryEINTR(waitpid)(pid, &status, 0); - MaybeSave(); - if (ret < 0) { - return PosixError(errno, "wait failed"); - } else if (ret != pid) { - return PosixError(ESRCH, absl::StrCat("wait got ", ret, " want ", pid)); - } - - if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGSTOP) { - return PosixError(EINVAL, - absl::StrCat("pid did not SIGSTOP; status = ", status)); - } - - return NoError(); -} - -// Returns a valid ELF that PTRACE_TRACEME and SIGSTOPs itself. -// -// UpdateOffsets must be called before writing this ELF. -ElfBinary<64> StandardElf() { - ElfBinary<64> elf; - elf.header.e_ident[EI_MAG0] = ELFMAG0; - elf.header.e_ident[EI_MAG1] = ELFMAG1; - elf.header.e_ident[EI_MAG2] = ELFMAG2; - elf.header.e_ident[EI_MAG3] = ELFMAG3; - elf.header.e_ident[EI_CLASS] = ELFCLASS64; - elf.header.e_ident[EI_DATA] = ELFDATA2LSB; - elf.header.e_ident[EI_VERSION] = EV_CURRENT; - elf.header.e_type = ET_EXEC; - elf.header.e_machine = EM_TYPE; - elf.header.e_version = EV_CURRENT; - elf.header.e_phoff = sizeof(elf.header); - elf.header.e_phentsize = sizeof(decltype(elf)::ElfPhdr); - - // TODO(gvisor.dev/issue/153): Always include a PT_GNU_STACK segment to - // disable executable stacks. With this omitted the stack (and all PROT_READ) - // mappings should be executable, but gVisor doesn't support that. - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_GNU_STACK; - phdr.p_flags = PF_R | PF_W; - elf.phdrs.push_back(phdr); - - phdr = {}; - phdr.p_type = PT_LOAD; - phdr.p_flags = PF_R | PF_X; - phdr.p_offset = 0; - phdr.p_vaddr = 0x40000; - phdr.p_filesz = sizeof(kPtraceCode); - phdr.p_memsz = phdr.p_filesz; - elf.phdrs.push_back(phdr); - - elf.header.e_entry = phdr.p_vaddr; - - elf.data.assign(kPtraceCode, kPtraceCode + sizeof(kPtraceCode)); - - return elf; -} - -// Test that a trivial binary executes. -TEST(ElfTest, Execute) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - // Ensure it made it to SIGSTOP. - ASSERT_NO_ERRNO(WaitStopped(child)); - - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - // RIP/PC is just beyond the final syscall instruction. - EXPECT_EQ(IP_REG(regs), elf.header.e_entry + sizeof(kPtraceCode)); - - EXPECT_THAT(child, ContainsMappings(std::vector<ProcMapsEntry>({ - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - file.path().c_str()}, - }))); -} - -// StandardElf without data completes execve, but faults once running. -TEST(ElfTest, MissingText) { - ElfBinary<64> elf = StandardElf(); - elf.data.clear(); - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - // It runs off the end of the zeroes filling the end of the page. -#if defined(__x86_64__) - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) << status; -#elif defined(__aarch64__) - // 0 is an invalid instruction opcode on arm64. - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGILL) << status; -#endif -} - -// Typical ELF with a data + bss segment -TEST(ElfTest, DataSegment) { - ElfBinary<64> elf = StandardElf(); - - // Create a standard ELF, but extend to 1.5 pages. The second page will be the - // beginning of a multi-page data + bss segment. - elf.data.resize(kPageSize + kPageSize / 2); - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - phdr.p_flags = PF_R | PF_W; - phdr.p_offset = kPageSize; - phdr.p_vaddr = 0x41000; - phdr.p_filesz = kPageSize / 2; - // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a - // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. - phdr.p_memsz = 2 * kPageSize - kPageSize / 2; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - EXPECT_THAT( - child, ContainsMappings(std::vector<ProcMapsEntry>({ - // text page. - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - file.path().c_str()}, - // data + bss page from file. - {0x41000, 0x42000, true, true, false, true, kPageSize, 0, 0, 0, - file.path().c_str()}, - // bss page from anon. - {0x42000, 0x43000, true, true, false, true, 0, 0, 0, 0, ""}, - }))); -} - -// Additonal pages beyond filesz honor (only) execute protections. -// -// N.B. Linux changed this in 4.11 (16e72e9b30986 "powerpc: do not make the -// entire heap executable"). Previously, extra pages were always RW. -TEST(ElfTest, ExtraMemPages) { - // gVisor has the newer behavior. - if (!IsRunningOnGvisor()) { - auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); - SKIP_IF(version.major < 4 || (version.major == 4 && version.minor < 11)); - } - - ElfBinary<64> elf = StandardElf(); - - // Create a standard ELF, but extend to 1.5 pages. The second page will be the - // beginning of a multi-page data + bss segment. - elf.data.resize(kPageSize + kPageSize / 2); - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - // RWX segment. The extra anon page will also be RWX. - // - // N.B. Linux uses clear_user to clear the end of the file-mapped page, which - // respects the mapping protections. Thus if we map this RO with memsz > - // (unaligned) filesz, then execve will fail with EFAULT. See padzero(elf_bss) - // in fs/binfmt_elf.c:load_elf_binary. - // - // N.N.B.B. The above only applies to the last segment. For earlier segments, - // the clear_user error is ignored. - phdr.p_flags = PF_R | PF_W | PF_X; - phdr.p_offset = kPageSize; - phdr.p_vaddr = 0x41000; - phdr.p_filesz = kPageSize / 2; - // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a - // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. - phdr.p_memsz = 2 * kPageSize - kPageSize / 2; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - EXPECT_THAT(child, - ContainsMappings(std::vector<ProcMapsEntry>({ - // text page. - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - file.path().c_str()}, - // data + bss page from file. - {0x41000, 0x42000, true, true, true, true, kPageSize, 0, 0, 0, - file.path().c_str()}, - // extra page from anon. - {0x42000, 0x43000, true, true, true, true, 0, 0, 0, 0, ""}, - }))); -} - -// An aligned segment with filesz == 0, memsz > 0 is anon-only. -TEST(ElfTest, AnonOnlySegment) { - ElfBinary<64> elf = StandardElf(); - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - // RO segment. The extra anon page will be RW anyways. - phdr.p_flags = PF_R; - phdr.p_offset = 0; - phdr.p_vaddr = 0x41000; - phdr.p_filesz = 0; - phdr.p_memsz = kPageSize; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - // UpdateOffsets adjusts p_vaddr and p_offset by the header size, but we need - // a page-aligned p_vaddr to get a truly anon-only page. - elf.phdrs[2].p_vaddr = 0x41000; - // N.B. p_offset is now unaligned, but Linux doesn't care since this is - // anon-only. - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - EXPECT_THAT(child, - ContainsMappings(std::vector<ProcMapsEntry>({ - // text page. - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - file.path().c_str()}, - // anon page. - {0x41000, 0x42000, true, true, false, true, 0, 0, 0, 0, ""}, - }))); -} - -// p_offset must have the same alignment as p_vaddr. -TEST(ElfTest, UnalignedOffset) { - ElfBinary<64> elf = StandardElf(); - - // Unaligned offset. - elf.phdrs[1].p_offset += 1; - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - - // execve(2) return EINVAL, but behavior varies between Linux and gVisor. - // - // On Linux, the new mm is committed before attempting to map into it. By the - // time we hit EINVAL in the segment mmap, the old mm is gone. Linux returns - // to an empty mm, which immediately segfaults. - // - // OTOH, gVisor maps into the new mm before committing it. Thus when it hits - // failure, the caller is still intact to receive the error. - if (IsRunningOnGvisor()) { - ASSERT_EQ(execve_errno, EINVAL); - } else { - ASSERT_EQ(execve_errno, 0); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) << status; - } -} - -// Linux will allow PT_LOAD segments to overlap. -TEST(ElfTest, DirectlyOverlappingSegments) { - // NOTE(b/37289926): see PIEOutOfOrderSegments. - SKIP_IF(IsRunningOnGvisor()); - - ElfBinary<64> elf = StandardElf(); - - // Same as the StandardElf mapping. - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - // Add PF_W so we can differentiate this mapping from the first. - phdr.p_flags = PF_R | PF_W | PF_X; - phdr.p_offset = 0; - phdr.p_vaddr = 0x40000; - phdr.p_filesz = sizeof(kPtraceCode); - phdr.p_memsz = phdr.p_filesz; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - EXPECT_THAT(child, ContainsMappings(std::vector<ProcMapsEntry>({ - {0x40000, 0x41000, true, true, true, true, 0, 0, 0, 0, - file.path().c_str()}, - }))); -} - -// Linux allows out-of-order PT_LOAD segments. -TEST(ElfTest, OutOfOrderSegments) { - // NOTE(b/37289926): see PIEOutOfOrderSegments. - SKIP_IF(IsRunningOnGvisor()); - - ElfBinary<64> elf = StandardElf(); - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - phdr.p_flags = PF_R | PF_X; - phdr.p_offset = 0; - phdr.p_vaddr = 0x20000; - phdr.p_filesz = sizeof(kPtraceCode); - phdr.p_memsz = phdr.p_filesz; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - EXPECT_THAT(child, ContainsMappings(std::vector<ProcMapsEntry>({ - {0x20000, 0x21000, true, false, true, true, 0, 0, 0, 0, - file.path().c_str()}, - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - file.path().c_str()}, - }))); -} - -// header.e_phoff is bound the end of the file. -TEST(ElfTest, OutOfBoundsPhdrs) { - ElfBinary<64> elf = StandardElf(); - elf.header.e_phoff = 0x100000; - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - // On Linux 3.11, this caused EIO. On newer Linux, it causes ENOEXEC. - EXPECT_THAT(execve_errno, AnyOf(Eq(ENOEXEC), Eq(EIO))); -} - -// Claim there is a phdr beyond the end of the file, but don't include it. -TEST(ElfTest, MissingPhdr) { - ElfBinary<64> elf = StandardElf(); - - // Clear data so the file ends immediately after the phdrs. - // N.B. Per ElfTest.MissingData, StandardElf without data completes execve - // without error. - elf.data.clear(); - elf.UpdateOffsets(); - - // Claim that there is another phdr just beyond the end of the file. Of - // course, it isn't accessible. - elf.header.e_phnum++; - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - // On Linux 3.11, this caused EIO. On newer Linux, it causes ENOEXEC. - EXPECT_THAT(execve_errno, AnyOf(Eq(ENOEXEC), Eq(EIO))); -} - -// No headers at all, just the ELF magic. -TEST(ElfTest, MissingHeader) { - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0755)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - const char kElfMagic[] = {0x7f, 'E', 'L', 'F'}; - - ASSERT_THAT(WriteFd(fd.get(), &kElfMagic, sizeof(kElfMagic)), - SyscallSucceeds()); - fd.reset(); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, ENOEXEC); -} - -// Load a PIE ELF with a data + bss segment. -TEST(ElfTest, PIE) { - ElfBinary<64> elf = StandardElf(); - - elf.header.e_type = ET_DYN; - - // Create a standard ELF, but extend to 1.5 pages. The second page will be the - // beginning of a multi-page data + bss segment. - elf.data.resize(kPageSize + kPageSize / 2); - - elf.header.e_entry = 0x0; - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - phdr.p_flags = PF_R | PF_W; - phdr.p_offset = kPageSize; - // Put the data segment at a bit of an offset. - phdr.p_vaddr = 0x20000; - phdr.p_filesz = kPageSize / 2; - // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a - // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. - phdr.p_memsz = 2 * kPageSize - kPageSize / 2; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - // The first segment really needs to start at 0 for a normal PIE binary, and - // thus includes the headers. - const uint64_t offset = elf.phdrs[1].p_offset; - elf.phdrs[1].p_offset = 0x0; - elf.phdrs[1].p_vaddr = 0x0; - elf.phdrs[1].p_filesz += offset; - elf.phdrs[1].p_memsz += offset; - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - // RIP tells us which page the first segment was loaded into. - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - - const uint64_t load_addr = IP_REG(regs) & ~(kPageSize - 1); - - EXPECT_THAT(child, ContainsMappings(std::vector<ProcMapsEntry>({ - // text page. - {load_addr, load_addr + 0x1000, true, false, true, - true, 0, 0, 0, 0, file.path().c_str()}, - // data + bss page from file. - {load_addr + 0x20000, load_addr + 0x21000, true, true, - false, true, kPageSize, 0, 0, 0, file.path().c_str()}, - // bss page from anon. - {load_addr + 0x21000, load_addr + 0x22000, true, true, - false, true, 0, 0, 0, 0, ""}, - }))); -} - -// PIE binary with a non-zero start address. -// -// This is non-standard for a PIE binary, but valid. The binary is still loaded -// at an arbitrary address, not the first PT_LOAD vaddr. -// -// N.B. Linux changed this behavior in d1fd836dcf00d2028c700c7e44d2c23404062c90. -// Previously, with "randomization" enabled, PIE binaries with a non-zero start -// address would be be loaded at the address they specified because mmap was -// passed the load address, which wasn't 0 as expected. -// -// This change is present in kernel v4.1+. -TEST(ElfTest, PIENonZeroStart) { - // gVisor has the newer behavior. - if (!IsRunningOnGvisor()) { - auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); - SKIP_IF(version.major < 4 || (version.major == 4 && version.minor < 1)); - } - - ElfBinary<64> elf = StandardElf(); - - elf.header.e_type = ET_DYN; - - // Create a standard ELF, but extend to 1.5 pages. The second page will be the - // beginning of a multi-page data + bss segment. - elf.data.resize(kPageSize + kPageSize / 2); - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - phdr.p_flags = PF_R | PF_W; - phdr.p_offset = kPageSize; - // Put the data segment at a bit of an offset. - phdr.p_vaddr = 0x60000; - phdr.p_filesz = kPageSize / 2; - // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a - // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. - phdr.p_memsz = 2 * kPageSize - kPageSize / 2; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - // RIP tells us which page the first segment was loaded into. - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - - const uint64_t load_addr = IP_REG(regs) & ~(kPageSize - 1); - - // The ELF is loaded at an arbitrary address, not the first PT_LOAD vaddr. - // - // N.B. this is technically flaky, but Linux is *extremely* unlikely to pick - // this as the start address, as it searches from the top down. - EXPECT_NE(load_addr, 0x40000); - - EXPECT_THAT(child, ContainsMappings(std::vector<ProcMapsEntry>({ - // text page. - {load_addr, load_addr + 0x1000, true, false, true, - true, 0, 0, 0, 0, file.path().c_str()}, - // data + bss page from file. - {load_addr + 0x20000, load_addr + 0x21000, true, true, - false, true, kPageSize, 0, 0, 0, file.path().c_str()}, - // bss page from anon. - {load_addr + 0x21000, load_addr + 0x22000, true, true, - false, true, 0, 0, 0, 0, ""}, - }))); -} - -TEST(ElfTest, PIEOutOfOrderSegments) { - // TODO(b/37289926): This triggers a bug in Linux where it computes the size - // of the binary as 0x20000 - 0x40000 = 0xfffffffffffe0000, which obviously - // fails to map. - // - // We test gVisor's behavior (of rejecting the binary) because I assert that - // Linux is wrong and needs to be fixed. - SKIP_IF(!IsRunningOnGvisor()); - - ElfBinary<64> elf = StandardElf(); - - elf.header.e_type = ET_DYN; - - // Create a standard ELF, but extend to 1.5 pages. The second page will be the - // beginning of a multi-page data + bss segment. - elf.data.resize(kPageSize + kPageSize / 2); - - decltype(elf)::ElfPhdr phdr = {}; - phdr.p_type = PT_LOAD; - phdr.p_flags = PF_R | PF_W; - phdr.p_offset = kPageSize; - // Put the data segment *before* the first segment. - phdr.p_vaddr = 0x20000; - phdr.p_filesz = kPageSize / 2; - // The header is going to push vaddr up by a few hundred bytes. Keep p_memsz a - // bit less than 2 pages so this mapping doesn't extend beyond 0x43000. - phdr.p_memsz = 2 * kPageSize - kPageSize / 2; - elf.phdrs.push_back(phdr); - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, ENOEXEC); -} - -TEST(ElfTest, PIEOverflow) { - ElfBinary<64> elf = StandardElf(); - - elf.header.e_type = ET_DYN; - - // Choose vaddr of the first segment so that the end address overflows if the - // segment is mapped with a non-zero offset. - elf.phdrs[1].p_vaddr = 0xfffffffffffff000UL - elf.phdrs[1].p_memsz; - - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - if (IsRunningOnGvisor()) { - ASSERT_EQ(execve_errno, EINVAL); - } else { - ASSERT_EQ(execve_errno, 0); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) << status; - } -} - -// Standard dynamically linked binary with an ELF interpreter. -TEST(ElfTest, ELFInterpreter) { - ElfBinary<64> interpreter = StandardElf(); - interpreter.header.e_type = ET_DYN; - interpreter.header.e_entry = 0x0; - interpreter.UpdateOffsets(); - - // The first segment really needs to start at 0 for a normal PIE binary, and - // thus includes the headers. - uint64_t const offset = interpreter.phdrs[1].p_offset; - // N.B. Since Linux 4.10 (0036d1f7eb95b "binfmt_elf: fix calculations for bss - // padding"), Linux unconditionally zeroes the remainder of the highest mapped - // page in an interpreter, failing if the protections don't allow write. Thus - // we must mark this writeable. - interpreter.phdrs[1].p_flags = PF_R | PF_W | PF_X; - interpreter.phdrs[1].p_offset = 0x0; - interpreter.phdrs[1].p_vaddr = 0x0; - interpreter.phdrs[1].p_filesz += offset; - interpreter.phdrs[1].p_memsz += offset; - - TempPath interpreter_file = - ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); - - ElfBinary<64> binary = StandardElf(); - - // Append the interpreter path. - int const interp_data_start = binary.data.size(); - for (char const c : interpreter_file.path()) { - binary.data.push_back(c); - } - // NUL-terminate. - binary.data.push_back(0); - int const interp_data_size = binary.data.size() - interp_data_start; - - decltype(binary)::ElfPhdr phdr = {}; - phdr.p_type = PT_INTERP; - phdr.p_offset = interp_data_start; - phdr.p_filesz = interp_data_size; - phdr.p_memsz = interp_data_size; - // "If [PT_INTERP] is present, it must precede any loadable segment entry." - // - // However, Linux allows it anywhere, so we just stick it at the end to make - // sure out-of-order PT_INTERP is OK. - binary.phdrs.push_back(phdr); - - binary.UpdateOffsets(); - - TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - // RIP tells us which page the first segment of the interpreter was loaded - // into. - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - - const uint64_t interp_load_addr = IP_REG(regs) & ~(kPageSize - 1); - - EXPECT_THAT( - child, ContainsMappings(std::vector<ProcMapsEntry>({ - // Main binary - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - binary_file.path().c_str()}, - // Interpreter - {interp_load_addr, interp_load_addr + 0x1000, true, true, true, - true, 0, 0, 0, 0, interpreter_file.path().c_str()}, - }))); -} - -// Test parameter to ElfInterpterStaticTest cases. The first item is a suffix to -// add to the end of the interpreter path in the PT_INTERP segment and the -// second is the expected execve(2) errno. -using ElfInterpreterStaticParam = std::tuple<std::vector<char>, int>; - -class ElfInterpreterStaticTest - : public ::testing::TestWithParam<ElfInterpreterStaticParam> {}; - -// Statically linked ELF with a statically linked ELF interpreter. -TEST_P(ElfInterpreterStaticTest, Test) { - // TODO(gvisor.dev/issue/3721): Test has been observed to segfault on 5.X - // kernels. - if (!IsRunningOnGvisor()) { - auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); - SKIP_IF(version.major > 4); - } - - const std::vector<char> segment_suffix = std::get<0>(GetParam()); - const int expected_errno = std::get<1>(GetParam()); - - ElfBinary<64> interpreter = StandardElf(); - // See comment in ElfTest.ELFInterpreter. - interpreter.phdrs[1].p_flags = PF_R | PF_W | PF_X; - interpreter.UpdateOffsets(); - TempPath interpreter_file = - ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); - - ElfBinary<64> binary = StandardElf(); - // The PT_LOAD segment conflicts with the interpreter's PT_LOAD segment. The - // interpreter's will be mapped directly over the binary's. - - // Interpreter path plus the parameterized suffix in the PT_INTERP segment. - const std::string path = interpreter_file.path(); - std::vector<char> segment(path.begin(), path.end()); - segment.insert(segment.end(), segment_suffix.begin(), segment_suffix.end()); - binary.AddInterpreter(segment); - - binary.UpdateOffsets(); - - TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, expected_errno); - - if (expected_errno == 0) { - ASSERT_NO_ERRNO(WaitStopped(child)); - - EXPECT_THAT(child, ContainsMappings(std::vector<ProcMapsEntry>({ - // Interpreter. - {0x40000, 0x41000, true, true, true, true, 0, 0, 0, - 0, interpreter_file.path().c_str()}, - }))); - } -} - -INSTANTIATE_TEST_SUITE_P( - Cases, ElfInterpreterStaticTest, - ::testing::ValuesIn({ - // Simple NUL-terminator to run the interpreter as normal. - std::make_tuple(std::vector<char>({'\0'}), 0), - // Add some garbage to the segment followed by a NUL-terminator. This is - // ignored. - std::make_tuple(std::vector<char>({'\0', 'b', '\0'}), 0), - // Add some garbage to the segment without a NUL-terminator. Linux will - // reject - // this. - std::make_tuple(std::vector<char>({'\0', 'b'}), ENOEXEC), - })); - -// Test parameter to ElfInterpterBadPathTest cases. The first item is the -// contents of the PT_INTERP segment and the second is the expected execve(2) -// errno. -using ElfInterpreterBadPathParam = std::tuple<std::vector<char>, int>; - -class ElfInterpreterBadPathTest - : public ::testing::TestWithParam<ElfInterpreterBadPathParam> {}; - -TEST_P(ElfInterpreterBadPathTest, Test) { - const std::vector<char> segment = std::get<0>(GetParam()); - const int expected_errno = std::get<1>(GetParam()); - - ElfBinary<64> binary = StandardElf(); - binary.AddInterpreter(segment); - binary.UpdateOffsets(); - - TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); - - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - binary_file.path(), {binary_file.path()}, {}, nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, expected_errno); -} - -INSTANTIATE_TEST_SUITE_P( - Cases, ElfInterpreterBadPathTest, - ::testing::ValuesIn({ - // NUL-terminated fake path in the PT_INTERP segment. - std::make_tuple(std::vector<char>({'/', 'f', '/', 'b', '\0'}), ENOENT), - // ELF interpreter not NUL-terminated. - std::make_tuple(std::vector<char>({'/', 'f', '/', 'b'}), ENOEXEC), - // ELF interpreter path omitted entirely. - // - // fs/binfmt_elf.c:load_elf_binary returns ENOEXEC if p_filesz is < 2 - // bytes. - std::make_tuple(std::vector<char>({'\0'}), ENOEXEC), - // ELF interpreter path = "\0". - // - // fs/binfmt_elf.c:load_elf_binary returns ENOEXEC if p_filesz is < 2 - // bytes, so add an extra byte to pass that check. - // - // load_elf_binary -> open_exec -> do_open_execat fails to check that - // name != '\0' before calling do_filp_open, which thus opens the - // working directory. do_open_execat returns EACCES because the - // directory is not a regular file. - std::make_tuple(std::vector<char>({'\0', '\0'}), EACCES), - })); - -// Relative path to ELF interpreter. -TEST(ElfTest, ELFInterpreterRelative) { - ElfBinary<64> interpreter = StandardElf(); - interpreter.header.e_type = ET_DYN; - interpreter.header.e_entry = 0x0; - interpreter.UpdateOffsets(); - - // The first segment really needs to start at 0 for a normal PIE binary, and - // thus includes the headers. - uint64_t const offset = interpreter.phdrs[1].p_offset; - // See comment in ElfTest.ELFInterpreter. - interpreter.phdrs[1].p_flags = PF_R | PF_W | PF_X; - interpreter.phdrs[1].p_offset = 0x0; - interpreter.phdrs[1].p_vaddr = 0x0; - interpreter.phdrs[1].p_filesz += offset; - interpreter.phdrs[1].p_memsz += offset; - - TempPath interpreter_file = - ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); - auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); - auto interpreter_relative = - ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, interpreter_file.path())); - - ElfBinary<64> binary = StandardElf(); - - // NUL-terminated path in the PT_INTERP segment. - std::vector<char> segment(interpreter_relative.begin(), - interpreter_relative.end()); - segment.push_back(0); - binary.AddInterpreter(segment); - - binary.UpdateOffsets(); - - TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - // RIP tells us which page the first segment of the interpreter was loaded - // into. - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - - const uint64_t interp_load_addr = IP_REG(regs) & ~(kPageSize - 1); - - EXPECT_THAT( - child, ContainsMappings(std::vector<ProcMapsEntry>({ - // Main binary - {0x40000, 0x41000, true, false, true, true, 0, 0, 0, 0, - binary_file.path().c_str()}, - // Interpreter - {interp_load_addr, interp_load_addr + 0x1000, true, true, true, - true, 0, 0, 0, 0, interpreter_file.path().c_str()}, - }))); -} - -// ELF interpreter architecture doesn't match the binary. -TEST(ElfTest, ELFInterpreterWrongArch) { - ElfBinary<64> interpreter = StandardElf(); - interpreter.header.e_machine = EM_PPC64; - interpreter.header.e_type = ET_DYN; - interpreter.header.e_entry = 0x0; - interpreter.UpdateOffsets(); - - // The first segment really needs to start at 0 for a normal PIE binary, and - // thus includes the headers. - uint64_t const offset = interpreter.phdrs[1].p_offset; - // See comment in ElfTest.ELFInterpreter. - interpreter.phdrs[1].p_flags = PF_R | PF_W | PF_X; - interpreter.phdrs[1].p_offset = 0x0; - interpreter.phdrs[1].p_vaddr = 0x0; - interpreter.phdrs[1].p_filesz += offset; - interpreter.phdrs[1].p_memsz += offset; - - TempPath interpreter_file = - ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); - - ElfBinary<64> binary = StandardElf(); - - // NUL-terminated path in the PT_INTERP segment. - const std::string path = interpreter_file.path(); - std::vector<char> segment(path.begin(), path.end()); - segment.push_back(0); - binary.AddInterpreter(segment); - - binary.UpdateOffsets(); - - TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - binary_file.path(), {binary_file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, ELIBBAD); -} - -// No execute permissions on the binary. -TEST(ElfTest, NoExecute) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - ASSERT_THAT(chmod(file.path().c_str(), 0644), SyscallSucceeds()); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, EACCES); -} - -// Execute, but no read permissions on the binary works just fine. -TEST(ElfTest, NoRead) { - // TODO(gvisor.dev/issue/160): gVisor's backing filesystem may prevent the - // sentry from reading the executable. - SKIP_IF(IsRunningOnGvisor()); - - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - ASSERT_THAT(chmod(file.path().c_str(), 0111), SyscallSucceeds()); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - ASSERT_NO_ERRNO(WaitStopped(child)); - - // TODO(gvisor.dev/issue/160): A task with a non-readable executable is marked - // non-dumpable, preventing access to proc files. gVisor does not implement - // this behavior. -} - -// No execute permissions on the ELF interpreter. -TEST(ElfTest, ElfInterpreterNoExecute) { - ElfBinary<64> interpreter = StandardElf(); - interpreter.header.e_type = ET_DYN; - interpreter.header.e_entry = 0x0; - interpreter.UpdateOffsets(); - - // The first segment really needs to start at 0 for a normal PIE binary, and - // thus includes the headers. - uint64_t const offset = interpreter.phdrs[1].p_offset; - // See comment in ElfTest.ELFInterpreter. - interpreter.phdrs[1].p_flags = PF_R | PF_W | PF_X; - interpreter.phdrs[1].p_offset = 0x0; - interpreter.phdrs[1].p_vaddr = 0x0; - interpreter.phdrs[1].p_filesz += offset; - interpreter.phdrs[1].p_memsz += offset; - - TempPath interpreter_file = - ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(interpreter)); - - ElfBinary<64> binary = StandardElf(); - - // NUL-terminated path in the PT_INTERP segment. - const std::string path = interpreter_file.path(); - std::vector<char> segment(path.begin(), path.end()); - segment.push_back(0); - binary.AddInterpreter(segment); - - binary.UpdateOffsets(); - - TempPath binary_file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(binary)); - - ASSERT_THAT(chmod(interpreter_file.path().c_str(), 0644), SyscallSucceeds()); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(interpreter_file.path(), {interpreter_file.path()}, {}, - &child, &execve_errno)); - EXPECT_EQ(execve_errno, EACCES); -} - -// Execute a basic interpreter script. -TEST(InterpreterScriptTest, Execute) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - EXPECT_NO_ERRNO(WaitStopped(child)); -} - -// Whitespace after #!. -TEST(InterpreterScriptTest, Whitespace) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#! \t \t", binary.path()), 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - EXPECT_NO_ERRNO(WaitStopped(child)); -} - -// Interpreter script is missing execute permission. -TEST(InterpreterScriptTest, InterpreterScriptNoExecute) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0644)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, EACCES); -} - -// Binary interpreter script refers to is missing execute permission. -TEST(InterpreterScriptTest, BinaryNoExecute) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - ASSERT_THAT(chmod(binary.path().c_str(), 0644), SyscallSucceeds()); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, EACCES); -} - -// Linux will load interpreter scripts five levels deep, but no more. -TEST(InterpreterScriptTest, MaxRecursion) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - TempPath script1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - "/tmp", absl::StrCat("#!", binary.path()), 0755)); - TempPath script2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - "/tmp", absl::StrCat("#!", script1.path()), 0755)); - TempPath script3 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - "/tmp", absl::StrCat("#!", script2.path()), 0755)); - TempPath script4 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - "/tmp", absl::StrCat("#!", script3.path()), 0755)); - TempPath script5 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - "/tmp", absl::StrCat("#!", script4.path()), 0755)); - TempPath script6 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - "/tmp", absl::StrCat("#!", script5.path()), 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script6.path(), {script6.path()}, {}, &child, &execve_errno)); - // Too many levels of recursion. - EXPECT_EQ(execve_errno, ELOOP); - - // The next level up is OK. - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script5.path(), {script5.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - EXPECT_NO_ERRNO(WaitStopped(child)); -} - -// Interpreter script with a relative path. -TEST(InterpreterScriptTest, RelativePath) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - auto cwd = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); - auto binary_relative = - ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(cwd, binary.path())); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary_relative), 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - EXPECT_NO_ERRNO(WaitStopped(child)); -} - -// Interpreter script with .. in a path component. -TEST(InterpreterScriptTest, UncleanPath) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!/tmp/../", binary.path()), - 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - EXPECT_NO_ERRNO(WaitStopped(child)); -} - -// Passed interpreter script is a symlink. -TEST(InterpreterScriptTest, Symlink) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - // Use /tmp explicitly to ensure the path is short enough. - TempPath binary = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith("/tmp", elf)); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", binary.path()), 0755)); - - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), script.path())); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(link.path(), {link.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - EXPECT_NO_ERRNO(WaitStopped(child)); -} - -// Interpreter script points to a symlink loop. -TEST(InterpreterScriptTest, SymlinkLoop) { - std::string const link1 = NewTempAbsPathInDir("/tmp"); - std::string const link2 = NewTempAbsPathInDir("/tmp"); - - ASSERT_THAT(symlink(link2.c_str(), link1.c_str()), SyscallSucceeds()); - auto remove_link1 = Cleanup( - [&link1] { EXPECT_THAT(unlink(link1.c_str()), SyscallSucceeds()); }); - - ASSERT_THAT(symlink(link1.c_str(), link2.c_str()), SyscallSucceeds()); - auto remove_link2 = Cleanup( - [&link2] { EXPECT_THAT(unlink(link2.c_str()), SyscallSucceeds()); }); - - TempPath script = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::StrCat("#!", link1), 0755)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(script.path(), {script.path()}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, ELOOP); -} - -// Binary is a symlink loop. -TEST(ExecveTest, SymlinkLoop) { - std::string const link1 = NewTempAbsPathInDir("/tmp"); - std::string const link2 = NewTempAbsPathInDir("/tmp"); - - ASSERT_THAT(symlink(link2.c_str(), link1.c_str()), SyscallSucceeds()); - auto remove_link = Cleanup( - [&link1] { EXPECT_THAT(unlink(link1.c_str()), SyscallSucceeds()); }); - - ASSERT_THAT(symlink(link1.c_str(), link2.c_str()), SyscallSucceeds()); - auto remove_link2 = Cleanup( - [&link2] { EXPECT_THAT(unlink(link2.c_str()), SyscallSucceeds()); }); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(link1, {link1}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, ELOOP); -} - -// Binary is a directory. -TEST(ExecveTest, Directory) { - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/tmp", {"/tmp"}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, EACCES); -} - -// Pass a valid binary as a directory (extra / on the end). -TEST(ExecveTest, BinaryAsDirectory) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - std::string const path = absl::StrCat(file.path(), "/"); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(path, {path}, {}, &child, &execve_errno)); - EXPECT_EQ(execve_errno, ENOTDIR); -} - -// The initial brk value is after the page at the end of the binary. -TEST(ExecveTest, BrkAfterBinary) { - ElfBinary<64> elf = StandardElf(); - elf.UpdateOffsets(); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf)); - - pid_t child; - int execve_errno; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno)); - ASSERT_EQ(execve_errno, 0); - - // Ensure it made it to SIGSTOP. - ASSERT_NO_ERRNO(WaitStopped(child)); - - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - - // RIP is just beyond the final syscall instruction. Rewind to execute a brk - // syscall. - IP_REG(regs) -= kSyscallSize; - RAX_REG(regs) = __NR_brk; - RDI_REG(regs) = 0; - ASSERT_THAT(ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - - // Resume the child, waiting for syscall entry. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child, 0, 0), SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << "status = " << status; - - // Execute the syscall. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child, 0, 0), SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << "status = " << status; - - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov), - SyscallSucceeds()); - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - - // brk is after the text page. - // - // The kernel does brk randomization, so we can't be sure what the exact - // address will be, but it is always beyond the final page in the binary. - // i.e., it does not start immediately after memsz in the middle of a page. - // Userspace may expect to use that space. - EXPECT_GE(RETURN_REG(regs), 0x41000); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/exec_proc_exe_workload.cc b/test/syscalls/linux/exec_proc_exe_workload.cc deleted file mode 100644 index 2989379b7..000000000 --- a/test/syscalls/linux/exec_proc_exe_workload.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdlib.h> -#include <unistd.h> - -#include <iostream> - -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" - -int main(int argc, char** argv, char** envp) { - // This is annoying. Because remote build systems may put these binaries - // in a content-addressable-store, you may wind up with /proc/self/exe - // pointing to some random path (but with a sensible argv[0]). - // - // Therefore, this test simply checks that the /proc/self/exe - // is absolute and *doesn't* match argv[1]. - std::string exe = - gvisor::testing::ProcessExePath(getpid()).ValueOrDie(); - if (exe[0] != '/') { - std::cerr << "relative path: " << exe << std::endl; - exit(1); - } - if (exe.find(argv[1]) != std::string::npos) { - std::cerr << "matching path: " << exe << std::endl; - exit(1); - } - - return 0; -} diff --git a/test/syscalls/linux/exec_state_workload.cc b/test/syscalls/linux/exec_state_workload.cc deleted file mode 100644 index 028902b14..000000000 --- a/test/syscalls/linux/exec_state_workload.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/auxv.h> -#include <sys/prctl.h> -#include <sys/time.h> - -#include <iostream> -#include <ostream> -#include <string> - -#include "absl/strings/numbers.h" - -// Pretty-print a sigset_t. -std::ostream& operator<<(std::ostream& out, const sigset_t& s) { - out << "{ "; - - for (int i = 0; i < NSIG; i++) { - if (sigismember(&s, i)) { - out << i << " "; - } - } - - out << "}"; - return out; -} - -// Verify that the signo handler is handler. -int CheckSigHandler(uint32_t signo, uintptr_t handler) { - struct sigaction sa; - int ret = sigaction(signo, nullptr, &sa); - if (ret < 0) { - perror("sigaction"); - return 1; - } - - if (reinterpret_cast<void (*)(int)>(handler) != sa.sa_handler) { - std::cerr << "signo " << signo << " handler got: " << sa.sa_handler - << " expected: " << std::hex << handler; - return 1; - } - return 0; -} - -// Verify that the signo is blocked. -int CheckSigBlocked(uint32_t signo) { - sigset_t s; - int ret = sigprocmask(SIG_SETMASK, nullptr, &s); - if (ret < 0) { - perror("sigprocmask"); - return 1; - } - - if (!sigismember(&s, signo)) { - std::cerr << "signal " << signo << " not blocked in signal mask: " << s - << std::endl; - return 1; - } - return 0; -} - -// Verify that the itimer is enabled. -int CheckItimerEnabled(uint32_t timer) { - struct itimerval itv; - int ret = getitimer(timer, &itv); - if (ret < 0) { - perror("getitimer"); - return 1; - } - - if (!itv.it_value.tv_sec && !itv.it_value.tv_usec && - !itv.it_interval.tv_sec && !itv.it_interval.tv_usec) { - std::cerr << "timer " << timer - << " not enabled. value sec: " << itv.it_value.tv_sec - << " usec: " << itv.it_value.tv_usec - << " interval sec: " << itv.it_interval.tv_sec - << " usec: " << itv.it_interval.tv_usec << std::endl; - return 1; - } - return 0; -} - -int PrintExecFn() { - unsigned long execfn = getauxval(AT_EXECFN); - if (!execfn) { - std::cerr << "AT_EXECFN missing" << std::endl; - return 1; - } - - std::cerr << reinterpret_cast<const char*>(execfn) << std::endl; - return 0; -} - -int PrintExecName() { - const size_t name_length = 20; - char name[name_length] = {0}; - if (prctl(PR_GET_NAME, name) < 0) { - std::cerr << "prctl(PR_GET_NAME) failed" << std::endl; - return 1; - } - - std::cerr << name << std::endl; - return 0; -} - -void usage(const std::string& prog) { - std::cerr << "usage:\n" - << "\t" << prog << " CheckSigHandler <signo> <handler addr (hex)>\n" - << "\t" << prog << " CheckSigBlocked <signo>\n" - << "\t" << prog << " CheckTimerDisabled <timer>\n" - << "\t" << prog << " PrintExecFn\n" - << "\t" << prog << " PrintExecName" << std::endl; -} - -int main(int argc, char** argv) { - if (argc < 2) { - usage(argv[0]); - return 1; - } - - std::string func(argv[1]); - - if (func == "CheckSigHandler") { - if (argc != 4) { - usage(argv[0]); - return 1; - } - - uint32_t signo; - if (!absl::SimpleAtoi(argv[2], &signo)) { - std::cerr << "invalid signo: " << argv[2] << std::endl; - return 1; - } - - uintptr_t handler; - if (!absl::numbers_internal::safe_strtoi_base(argv[3], &handler, 16)) { - std::cerr << "invalid handler: " << std::hex << argv[3] << std::endl; - return 1; - } - - return CheckSigHandler(signo, handler); - } - - if (func == "CheckSigBlocked") { - if (argc != 3) { - usage(argv[0]); - return 1; - } - - uint32_t signo; - if (!absl::SimpleAtoi(argv[2], &signo)) { - std::cerr << "invalid signo: " << argv[2] << std::endl; - return 1; - } - - return CheckSigBlocked(signo); - } - - if (func == "CheckItimerEnabled") { - if (argc != 3) { - usage(argv[0]); - return 1; - } - - uint32_t timer; - if (!absl::SimpleAtoi(argv[2], &timer)) { - std::cerr << "invalid signo: " << argv[2] << std::endl; - return 1; - } - - return CheckItimerEnabled(timer); - } - - if (func == "PrintExecFn") { - // N.B. This will be called as an interpreter script, with the script passed - // as the third argument. We don't care about that script. - return PrintExecFn(); - } - - if (func == "PrintExecName") { - // N.B. This may be called as an interpreter script like PrintExecFn. - return PrintExecName(); - } - - std::cerr << "Invalid function: " << func << std::endl; - return 1; -} diff --git a/test/syscalls/linux/exit.cc b/test/syscalls/linux/exit.cc deleted file mode 100644 index d52ea786b..000000000 --- a/test/syscalls/linux/exit.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/wait.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" -#include "test/util/time_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void TestExit(int code) { - pid_t pid = fork(); - if (pid == 0) { - _exit(code); - } - - ASSERT_THAT(pid, SyscallSucceeds()); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == code) << status; -} - -TEST(ExitTest, Success) { TestExit(0); } - -TEST(ExitTest, Failure) { TestExit(1); } - -// This test ensures that a process's file descriptors are closed when it calls -// exit(). In order to test this, the parent tries to read from a pipe whose -// write end is held by the child. While the read is blocking, the child exits, -// which should cause the parent to read 0 bytes due to EOF. -TEST(ExitTest, CloseFds) { - int pipe_fds[2]; - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - - FileDescriptor read_fd(pipe_fds[0]); - FileDescriptor write_fd(pipe_fds[1]); - - pid_t pid = fork(); - if (pid == 0) { - read_fd.reset(); - - SleepSafe(absl::Seconds(10)); - - _exit(0); - } - - EXPECT_THAT(pid, SyscallSucceeds()); - - write_fd.reset(); - - char buf[10]; - EXPECT_THAT(ReadFd(read_fd.get(), buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/exit_script.sh b/test/syscalls/linux/exit_script.sh deleted file mode 100755 index 527518e06..000000000 --- a/test/syscalls/linux/exit_script.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Copyright 2018 The gVisor Authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -if [ $# -ne 1 ]; then - echo "Usage: $0 exit_code" - exit 255 -fi - -exit $1 diff --git a/test/syscalls/linux/fadvise64.cc b/test/syscalls/linux/fadvise64.cc deleted file mode 100644 index ac24c4066..000000000 --- a/test/syscalls/linux/fadvise64.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <syscall.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -TEST(FAdvise64Test, Basic) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - // fadvise64 is noop in gVisor, so just test that it succeeds. - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_NORMAL), - SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_RANDOM), - SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_SEQUENTIAL), - SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_WILLNEED), - SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_DONTNEED), - SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_NOREUSE), - SyscallSucceeds()); -} - -TEST(FAdvise64Test, FAdvise64WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_NORMAL), - SyscallFailsWithErrno(EBADF)); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, POSIX_FADV_NORMAL), - SyscallFailsWithErrno(EBADF)); -} - -TEST(FAdvise64Test, InvalidArgs) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - // Note: offset is allowed to be negative. - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, static_cast<off_t>(-1), - POSIX_FADV_NORMAL), - SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(syscall(__NR_fadvise64, fd.get(), 0, 10, 12345), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(FAdvise64Test, NoPipes) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor read(fds[0]); - const FileDescriptor write(fds[1]); - - ASSERT_THAT(syscall(__NR_fadvise64, read.get(), 0, 10, POSIX_FADV_NORMAL), - SyscallFailsWithErrno(ESPIPE)); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fallocate.cc b/test/syscalls/linux/fallocate.cc deleted file mode 100644 index 5c839447e..000000000 --- a/test/syscalls/linux/fallocate.cc +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/eventfd.h> -#include <sys/resource.h> -#include <sys/signalfd.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/timerfd.h> -#include <syscall.h> -#include <time.h> -#include <unistd.h> - -#include <ctime> - -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/file_base.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/cleanup.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -int fallocate(int fd, int mode, off_t offset, off_t len) { - return RetryEINTR(syscall)(__NR_fallocate, fd, mode, offset, len); -} - -class AllocateTest : public FileTest { - void SetUp() override { FileTest::SetUp(); } -}; - -TEST_F(AllocateTest, Fallocate) { - // Check that it starts at size zero. - struct stat buf; - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); - - // Grow to ten bytes. - ASSERT_THAT(fallocate(test_file_fd_.get(), 0, 0, 10), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 10); - - // Allocate to a smaller size should be noop. - ASSERT_THAT(fallocate(test_file_fd_.get(), 0, 0, 5), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 10); - - // Grow again. - ASSERT_THAT(fallocate(test_file_fd_.get(), 0, 0, 20), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 20); - - // Grow with offset. - ASSERT_THAT(fallocate(test_file_fd_.get(), 0, 10, 20), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 30); - - // Grow with offset beyond EOF. - ASSERT_THAT(fallocate(test_file_fd_.get(), 0, 39, 1), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 40); - - // Given length 0 should fail with EINVAL. - ASSERT_THAT(fallocate(test_file_fd_.get(), 0, 50, 0), - SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 40); -} - -TEST_F(AllocateTest, FallocateInvalid) { - // Invalid FD - EXPECT_THAT(fallocate(-1, 0, 0, 10), SyscallFailsWithErrno(EBADF)); - - // Negative offset and size. - EXPECT_THAT(fallocate(test_file_fd_.get(), 0, -1, 10), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(fallocate(test_file_fd_.get(), 0, 0, -1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(fallocate(test_file_fd_.get(), 0, -1, -1), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(AllocateTest, FallocateReadonly) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - EXPECT_THAT(fallocate(fd.get(), 0, 0, 10), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(AllocateTest, FallocateWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - EXPECT_THAT(fallocate(fd.get(), 0, 0, 10), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(AllocateTest, FallocatePipe) { - int pipes[2]; - EXPECT_THAT(pipe(pipes), SyscallSucceeds()); - auto cleanup = Cleanup([&pipes] { - EXPECT_THAT(close(pipes[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipes[1]), SyscallSucceeds()); - }); - - EXPECT_THAT(fallocate(pipes[1], 0, 0, 10), SyscallFailsWithErrno(ESPIPE)); -} - -TEST_F(AllocateTest, FallocateChar) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_RDWR)); - EXPECT_THAT(fallocate(fd.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV)); -} - -TEST_F(AllocateTest, FallocateRlimit) { - // Get the current rlimit and restore after test run. - struct rlimit initial_lim; - ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - auto cleanup = Cleanup([&initial_lim] { - EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - }); - - // Try growing past the file size limit. - sigset_t new_mask; - sigemptyset(&new_mask); - sigaddset(&new_mask, SIGXFSZ); - sigprocmask(SIG_BLOCK, &new_mask, nullptr); - - struct rlimit setlim = {}; - setlim.rlim_cur = 1024; - setlim.rlim_max = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); - - EXPECT_THAT(fallocate(test_file_fd_.get(), 0, 0, 1025), - SyscallFailsWithErrno(EFBIG)); - - struct timespec timelimit = {}; - timelimit.tv_sec = 10; - EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ); - ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds()); -} - -TEST_F(AllocateTest, FallocateOtherFDs) { - int fd; - ASSERT_THAT(fd = timerfd_create(CLOCK_MONOTONIC, 0), SyscallSucceeds()); - auto timer_fd = FileDescriptor(fd); - EXPECT_THAT(fallocate(timer_fd.get(), 0, 0, 10), - SyscallFailsWithErrno(ENODEV)); - - sigset_t mask; - sigemptyset(&mask); - ASSERT_THAT(fd = signalfd(-1, &mask, 0), SyscallSucceeds()); - auto sfd = FileDescriptor(fd); - EXPECT_THAT(fallocate(sfd.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV)); - - auto efd = - ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE)); - EXPECT_THAT(fallocate(efd.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV)); - - auto sockfd = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - EXPECT_THAT(fallocate(sockfd.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV)); - - int socks[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, socks), - SyscallSucceeds()); - auto sock0 = FileDescriptor(socks[0]); - auto sock1 = FileDescriptor(socks[1]); - EXPECT_THAT(fallocate(sock0.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV)); - - int pipefds[2]; - ASSERT_THAT(pipe(pipefds), SyscallSucceeds()); - EXPECT_THAT(fallocate(pipefds[1], 0, 0, 10), SyscallFailsWithErrno(ESPIPE)); - close(pipefds[0]); - close(pipefds[1]); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fault.cc b/test/syscalls/linux/fault.cc deleted file mode 100644 index bd87d5e60..000000000 --- a/test/syscalls/linux/fault.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#define _GNU_SOURCE 1 -#include <signal.h> -#include <ucontext.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -__attribute__((noinline)) void Fault(void) { - volatile int* foo = nullptr; - *foo = 0; -} - -int GetPcFromUcontext(ucontext_t* uc, uintptr_t* pc) { -#if defined(__x86_64__) - *pc = uc->uc_mcontext.gregs[REG_RIP]; - return 1; -#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 -} - -void sigact_handler(int sig, siginfo_t* siginfo, void* context) { - uintptr_t pc; - if (GetPcFromUcontext(reinterpret_cast<ucontext_t*>(context), &pc)) { - /* Expect Fault() to be at most 64 bytes in size. */ - uintptr_t fault_addr = reinterpret_cast<uintptr_t>(&Fault); - EXPECT_GE(pc, fault_addr); - EXPECT_LT(pc, fault_addr + 64); - - // The following file is used to detect tests that exit prematurely. Since - // we need to call exit() here, delete the file by hand. - const char* exit_file = getenv("TEST_PREMATURE_EXIT_FILE"); - if (exit_file != nullptr) { - ASSERT_THAT(unlink(exit_file), SyscallSucceeds()); - } - exit(0); - } -} - -TEST(FaultTest, InRange) { - // Reset the signal handler to do nothing so that it doesn't freak out - // the test runner when we fire an alarm. - struct sigaction sa = {}; - sa.sa_sigaction = sigact_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - ASSERT_THAT(sigaction(SIGSEGV, &sa, nullptr), SyscallSucceeds()); - - Fault(); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fchdir.cc b/test/syscalls/linux/fchdir.cc deleted file mode 100644 index c6675802d..000000000 --- a/test/syscalls/linux/fchdir.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "test/util/capability_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(FchdirTest, Success) { - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - int fd; - ASSERT_THAT(fd = open(temp_dir.path().c_str(), O_DIRECTORY | O_RDONLY), - SyscallSucceeds()); - - EXPECT_THAT(fchdir(fd), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - // Change CWD to a permanent location as temp dirs will be cleaned up. - EXPECT_THAT(chdir("/"), SyscallSucceeds()); -} - -TEST(FchdirTest, InvalidFD) { - EXPECT_THAT(fchdir(-1), SyscallFailsWithErrno(EBADF)); -} - -TEST(FchdirTest, PermissionDenied) { - // Drop capabilities that allow us to override directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); - - int fd; - ASSERT_THAT(fd = open(temp_dir.path().c_str(), O_DIRECTORY | O_RDONLY), - SyscallSucceeds()); - - EXPECT_THAT(fchdir(fd), SyscallFailsWithErrno(EACCES)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(FchdirTest, NotDir) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - int fd; - ASSERT_THAT(fd = open(temp_file.path().c_str(), O_CREAT | O_RDONLY, 0777), - SyscallSucceeds()); - - EXPECT_THAT(fchdir(fd), SyscallFailsWithErrno(ENOTDIR)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(FchdirTest, FchdirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_dir.path(), O_PATH)); - ASSERT_THAT(open(temp_dir.path().c_str(), O_DIRECTORY | O_PATH), - SyscallSucceeds()); - - EXPECT_THAT(fchdir(fd.get()), SyscallSucceeds()); - // Change CWD to a permanent location as temp dirs will be cleaned up. - EXPECT_THAT(chdir("/"), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc deleted file mode 100644 index 4fa6751ff..000000000 --- a/test/syscalls/linux/fcntl.cc +++ /dev/null @@ -1,1999 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <signal.h> -#include <sys/epoll.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <syscall.h> -#include <unistd.h> - -#include <atomic> -#include <deque> -#include <iostream> -#include <list> -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "absl/base/port.h" -#include "absl/flags/flag.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/signal_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -ABSL_FLAG(std::string, child_set_lock_on, "", - "Contains the path to try to set a file lock on."); -ABSL_FLAG(bool, child_set_lock_write, false, - "Whether to set a writable lock (otherwise readable)"); -ABSL_FLAG(bool, blocking, false, - "Whether to set a blocking lock (otherwise non-blocking)."); -ABSL_FLAG(bool, retry_eintr, false, - "Whether to retry in the subprocess on EINTR."); -ABSL_FLAG(uint64_t, child_set_lock_start, 0, "The value of struct flock start"); -ABSL_FLAG(uint64_t, child_set_lock_len, 0, "The value of struct flock len"); -ABSL_FLAG(int32_t, socket_fd, -1, - "A socket to use for communicating more state back " - "to the parent."); - -namespace gvisor { -namespace testing { - -std::function<void(int, siginfo_t*, void*)> setsig_signal_handle; -void setsig_signal_handler(int signum, siginfo_t* siginfo, void* ucontext) { - setsig_signal_handle(signum, siginfo, ucontext); -} - -class FcntlLockTest : public ::testing::Test { - public: - void SetUp() override { - // Let's make a socket pair. - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, fds_), SyscallSucceeds()); - } - - void TearDown() override { - EXPECT_THAT(close(fds_[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds_[1]), SyscallSucceeds()); - } - - int64_t GetSubprocessFcntlTimeInUsec() { - int64_t ret = 0; - EXPECT_THAT(ReadFd(fds_[0], reinterpret_cast<void*>(&ret), sizeof(ret)), - SyscallSucceedsWithValue(sizeof(ret))); - return ret; - } - - // The first fd will remain with the process creating the subprocess - // and the second will go to the subprocess. - int fds_[2] = {}; -}; - -struct SignalDelivery { - int num; - siginfo_t info; -}; - -class FcntlSignalTest : public ::testing::Test { - public: - void SetUp() override { - int pipe_fds[2]; - ASSERT_THAT(pipe2(pipe_fds, O_NONBLOCK), SyscallSucceeds()); - pipe_read_fd_ = pipe_fds[0]; - pipe_write_fd_ = pipe_fds[1]; - } - - PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) { - struct sigaction handler; - handler.sa_sigaction = setsig_signal_handler; - setsig_signal_handle = [&](int signum, siginfo_t* siginfo, - void* unused_ucontext) { - SignalDelivery sig; - sig.num = signum; - sig.info = *siginfo; - signals_received_.push_back(sig); - num_signals_received_++; - }; - sigemptyset(&handler.sa_mask); - handler.sa_flags = SA_SIGINFO; - return ScopedSigaction(signum, handler); - } - - void FlushAndCloseFD(int fd) { - char buf; - int read_bytes; - do { - read_bytes = read(fd, &buf, 1); - } while (read_bytes > 0); - // read() can also fail with EWOULDBLOCK since the pipe is open in - // non-blocking mode. This is not an error. - EXPECT_TRUE(read_bytes == 0 || (read_bytes == -1 && errno == EWOULDBLOCK)); - EXPECT_THAT(close(fd), SyscallSucceeds()); - } - - void DupReadFD() { - ASSERT_THAT(pipe_read_fd_dup_ = dup(pipe_read_fd_), SyscallSucceeds()); - max_expected_signals++; - } - - void RegisterFD(int fd, int signum) { - ASSERT_THAT(fcntl(fd, F_SETOWN, getpid()), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd, F_SETSIG, signum), SyscallSucceeds()); - int old_flags; - ASSERT_THAT(old_flags = fcntl(fd, F_GETFL), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd, F_SETFL, old_flags | O_ASYNC), SyscallSucceeds()); - } - - void GenerateIOEvent() { - ASSERT_THAT(write(pipe_write_fd_, "test", 4), SyscallSucceedsWithValue(4)); - } - - void WaitForSignalDelivery(absl::Duration timeout) { - absl::Time wait_start = absl::Now(); - while (num_signals_received_ < max_expected_signals && - absl::Now() - wait_start < timeout) { - absl::SleepFor(absl::Milliseconds(10)); - } - } - - int pipe_read_fd_ = -1; - int pipe_read_fd_dup_ = -1; - int pipe_write_fd_ = -1; - int max_expected_signals = 1; - std::deque<SignalDelivery> signals_received_; - std::atomic<int> num_signals_received_ = 0; -}; - -namespace { - -PosixErrorOr<Cleanup> SubprocessLock(std::string const& path, bool for_write, - bool blocking, bool retry_eintr, int fd, - off_t start, off_t length, pid_t* child) { - std::vector<std::string> args = { - "/proc/self/exe", "--child_set_lock_on", path, - "--child_set_lock_start", absl::StrCat(start), "--child_set_lock_len", - absl::StrCat(length), "--socket_fd", absl::StrCat(fd)}; - - if (for_write) { - args.push_back("--child_set_lock_write"); - } - - if (blocking) { - args.push_back("--blocking"); - } - - if (retry_eintr) { - args.push_back("--retry_eintr"); - } - - int execve_errno = 0; - ASSIGN_OR_RETURN_ERRNO( - auto cleanup, - ForkAndExec("/proc/self/exe", ExecveArray(args.begin(), args.end()), {}, - nullptr, child, &execve_errno)); - - if (execve_errno != 0) { - return PosixError(execve_errno, "execve"); - } - - return std::move(cleanup); -} - -TEST(FcntlTest, FcntlDupWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); - - int new_fd; - // Dup the descriptor and make sure it's the same file. - EXPECT_THAT(new_fd = fcntl(fd.get(), F_DUPFD, 0), SyscallSucceeds()); - - FileDescriptor nfd = FileDescriptor(new_fd); - ASSERT_NE(fd.get(), nfd.get()); - ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); - EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(O_PATH)); -} - -TEST(FcntlTest, SetFileStatusFlagWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - - EXPECT_THAT(fcntl(fd.get(), F_SETFL, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(FcntlTest, BadFcntlsWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - - EXPECT_THAT(fcntl(fd.get(), F_SETOWN, 0), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(fcntl(fd.get(), F_GETOWN, 0), SyscallFailsWithErrno(EBADF)); - - EXPECT_THAT(fcntl(fd.get(), F_SETOWN_EX, 0), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(fcntl(fd.get(), F_GETOWN_EX, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(FcntlTest, SetCloExecBadFD) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. - FileDescriptor f = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - auto fd = f.get(); - f.reset(); - ASSERT_THAT(fcntl(fd, F_GETFD), SyscallFailsWithErrno(EBADF)); - ASSERT_THAT(fcntl(fd, F_SETFD, FD_CLOEXEC), SyscallFailsWithErrno(EBADF)); -} - -TEST(FcntlTest, SetCloExec) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Set the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(FcntlTest, SetCloExecWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - // Open a file descriptor with FD_CLOEXEC descriptor flag not set. - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Set the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(FcntlTest, DupFDCloExecWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - // Open a file descriptor with FD_CLOEXEC descriptor flag not set. - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - int nfd; - ASSERT_THAT(nfd = fcntl(fd.get(), F_DUPFD_CLOEXEC, 0), SyscallSucceeds()); - FileDescriptor dup_fd(nfd); - - // Check for the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(dup_fd.get(), F_GETFD), - SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST(FcntlTest, ClearCloExec) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_CLOEXEC)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - - // Clear the FD_CLOEXEC flag. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, 0), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, IndependentDescriptorFlags) { - // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Duplicate the descriptor. Ensure that it also doesn't have FD_CLOEXEC. - FileDescriptor newfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - ASSERT_THAT(fcntl(newfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); - - // Set FD_CLOEXEC on the first FD. - ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - - // Ensure that the second FD is unaffected by the change on the first. - ASSERT_THAT(fcntl(newfd.get(), F_GETFD), SyscallSucceedsWithValue(0)); -} - -// All file description flags passed to open appear in F_GETFL. -TEST(FcntlTest, GetAllFlags) { - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - int flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND; - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), flags)); - - // Linux forces O_LARGEFILE on all 64-bit kernels and gVisor's is 64-bit. - int expected = flags | kOLargeFile; - - int rflags; - EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(rflags, expected); -} - -// When O_PATH is specified in flags, flag bits other than O_CLOEXEC, -// O_DIRECTORY, and O_NOFOLLOW are ignored. -TEST(FcntlTest, GetOpathFlag) { - SKIP_IF(IsRunningWithVFS1()); - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - int flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND | O_PATH | - O_NOFOLLOW | O_DIRECTORY; - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), flags)); - - int expected = O_PATH | O_NOFOLLOW | O_DIRECTORY; - - int rflags; - EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(rflags, expected); -} - -TEST(FcntlTest, SetFlags) { - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), 0)); - - int const flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND; - EXPECT_THAT(fcntl(fd.get(), F_SETFL, flags), SyscallSucceeds()); - - // Can't set O_RDWR or O_SYNC. - // Linux forces O_LARGEFILE on all 64-bit kernels and gVisor's is 64-bit. - int expected = O_DIRECT | O_NONBLOCK | O_APPEND | kOLargeFile; - - int rflags; - EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(rflags, expected); -} - -void TestLock(int fd, short lock_type = F_RDLCK) { // NOLINT, type in flock - struct flock fl; - fl.l_type = lock_type; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // len 0 locks all bytes despite how large the file grows. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd, F_SETLK, &fl), SyscallSucceeds()); -} - -void TestLockBadFD(int fd, - short lock_type = F_RDLCK) { // NOLINT, type in flock - struct flock fl; - fl.l_type = lock_type; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // len 0 locks all bytes despite how large the file grows. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd, F_SETLK, &fl), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockBadFd) { TestLockBadFD(-1); } - -TEST_F(FcntlLockTest, SetLockDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0000)); - TestLock(fd.get()); -} - -TEST_F(FcntlLockTest, SetLockSymlink) { - // TODO(gvisor.dev/issue/2782): Replace with IsRunningWithVFS1() when O_PATH - // is supported. - SKIP_IF(IsRunningOnGvisor()); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto symlink = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), file.path())); - - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(symlink.path(), O_RDONLY | O_PATH, 0000)); - TestLockBadFD(fd.get()); -} - -TEST_F(FcntlLockTest, SetLockProc) { - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/status", O_RDONLY, 0000)); - TestLock(fd.get()); -} - -TEST_F(FcntlLockTest, SetLockPipe) { - SKIP_IF(IsRunningWithVFS1()); - - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - TestLock(fds[0]); - TestLockBadFD(fds[0], F_WRLCK); - - TestLock(fds[1], F_WRLCK); - TestLockBadFD(fds[1]); - - EXPECT_THAT(close(fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds[1]), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetLockSocket) { - SKIP_IF(IsRunningWithVFS1()); - - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - ASSERT_THAT(sock, SyscallSucceeds()); - - struct sockaddr_un addr = - ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(true /* abstract */, AF_UNIX)); - ASSERT_THAT( - bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceeds()); - - TestLock(sock); - EXPECT_THAT(close(sock), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetLockBadOpenFlagsWrite) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY, 0666)); - - struct flock fl0; - fl0.l_type = F_WRLCK; - fl0.l_whence = SEEK_SET; - fl0.l_start = 0; - fl0.l_len = 0; // Lock all file - - // Expect that setting a write lock using a read only file descriptor - // won't work. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockBadOpenFlagsRead) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY, 0666)); - - struct flock fl1; - fl1.l_type = F_RDLCK; - fl1.l_whence = SEEK_SET; - fl1.l_start = 0; - // Same as SetLockBadFd. - fl1.l_len = 0; - - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - struct flock fl0; - fl0.l_type = F_WRLCK; - fl0.l_whence = SEEK_SET; - fl0.l_start = 0; - fl0.l_len = 0; // Lock all file - - // Expect that setting a write lock using a Opath file descriptor - // won't work. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(FcntlLockTest, SetLockUnlockOnNothing) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_UNLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetWriteLockSingleProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd0 = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - EXPECT_THAT(fcntl(fd0.get(), F_SETLK, &fl), SyscallSucceeds()); - // Expect to be able to take the same lock on the same fd no problem. - EXPECT_THAT(fcntl(fd0.get(), F_SETLK, &fl), SyscallSucceeds()); - - FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // Expect to be able to take the same lock from a different fd but for - // the same process. - EXPECT_THAT(fcntl(fd1.get(), F_SETLK, &fl), SyscallSucceeds()); -} - -TEST_F(FcntlLockTest, SetReadLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // spawn a child process to take a read lock on the same file. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetReadThenWriteLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Assert that another process trying to lock on the same file will fail - // with EAGAIN. It's important that we keep the fd above open so that - // that the other process will contend with the lock. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Close the fd: we want to test that another process can acquire the - // lock after this point. - fd.reset(); - // Assert that another process can now acquire the lock. - - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetWriteThenReadLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - // Same as SetReadThenWriteLockMultiProc. - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadThenWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadThenWriteLockMultiProc. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Same as SetReadThenWriteLockMultiProc. - fd.reset(); // Close the fd. - - // Same as SetReadThenWriteLockMultiProc. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetWriteLockMultiProc) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - // Same as SetReadThenWriteLockMultiProc. - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadWriteLockMultiProc. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - fd.reset(); // Close the FD. - // Same as SetReadWriteLockMultiProc. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockIsRegional) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 4096; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadWriteLockMultiProc. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_len, 0, &child_pid)); - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockUpgradeDowngrade) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Upgrade to a write lock. This will prevent anyone else from taking - // the lock. - fl.l_type = F_WRLCK; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Same as SetReadWriteLockMultiProc., - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Downgrade back to a read lock. - fl.l_type = F_RDLCK; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Do the same stint as before, but this time it should succeed. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), false /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockDroppedOnClose) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // While somewhat surprising, obtaining another fd to the same file and - // then closing it in this process drops *all* locks. - FileDescriptor other_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - // Same as SetReadThenWriteLockMultiProc. - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Same as SetReadWriteLockMultiProc. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - other_fd.reset(); // Close. - - // Expect to be able to get the lock, given that the close above dropped it. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(file.path(), true /* write lock */, - false /* nonblocking */, false /* no eintr retry */, - -1 /* no socket fd */, fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockUnlock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // Setup two regional locks with different permissions. - struct flock fl0; - fl0.l_type = F_WRLCK; - fl0.l_whence = SEEK_SET; - fl0.l_start = 0; - fl0.l_len = 4096; - - struct flock fl1; - fl1.l_type = F_RDLCK; - fl1.l_whence = SEEK_SET; - fl1.l_start = 4096; - // Same as SetLockBadFd. - fl1.l_len = 0; - - // Set both region locks. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallSucceeds()); - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallSucceeds()); - - // Another process should fail to take a read lock on the entire file - // due to the regional write lock. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, -1 /* no socket fd */, 0, 0, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; - - // Then only unlock the writable one. This should ensure that other - // processes can take any read lock that it wants. - fl0.l_type = F_UNLCK; - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallSucceeds()); - - // Another process should now succeed to get a read lock on the entire file. - child_pid = 0; - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, -1 /* no socket fd */, 0, 0, &child_pid)); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST_F(FcntlLockTest, SetLockAcrossRename) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - // Setup two regional locks with different permissions. - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - // Same as SetLockBadFd. - fl.l_len = 0; - - // Set the region lock. - EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - // Rename the file to someplace nearby. - std::string const newpath = NewTempAbsPath(); - EXPECT_THAT(rename(file.path().c_str(), newpath.c_str()), SyscallSucceeds()); - - // Another process should fail to take a read lock on the renamed file - // since we still have an open handle to the inode. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - SubprocessLock(newpath, false /* write lock */, false /* nonblocking */, - false /* no eintr retry */, -1 /* no socket fd */, - fl.l_start, fl.l_len, &child_pid)); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == EAGAIN) - << "Exited with code: " << status; -} - -// NOTE: The blocking tests below aren't perfect. It's hard to assert exactly -// what the kernel did while handling a syscall. These tests are timing based -// because there really isn't any other reasonable way to assert that correct -// blocking behavior happened. - -// This test will verify that blocking works as expected when another process -// holds a write lock when obtaining a write lock. This test will hold the lock -// for some amount of time and then wait for the second process to send over the -// socket_fd the amount of time it was blocked for before the lock succeeded. -TEST_F(FcntlLockTest, SetWriteLockThenBlockingWriteLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the write lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. This will immediately block - // so we will release our lock after some amount of time and then assert the - // amount of time the other process was blocked for. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, fds_[1] /* Socket fd for timing information */, - fl.l_start, fl.l_len, &child_pid)); - - // We will wait kHoldLockForSec before we release our lock allowing the - // subprocess to obtain it. - constexpr absl::Duration kHoldLockFor = absl::Seconds(5); - const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); - - absl::SleepFor(kHoldLockFor); - - // Unlock our write lock. - fl.l_type = F_UNLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Read the blocked time from the subprocess socket. - int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); - - // We must have been waiting at least kMinBlockTime. - EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); - - // The FCNTL write lock must always succeed as it will simply block until it - // can obtain the lock. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// This test will verify that blocking works as expected when another process -// holds a read lock when obtaining a write lock. This test will hold the lock -// for some amount of time and then wait for the second process to send over the -// socket_fd the amount of time it was blocked for before the lock succeeded. -TEST_F(FcntlLockTest, SetReadLockThenBlockingWriteLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the write lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. This will immediately block - // so we will release our lock after some amount of time and then assert the - // amount of time the other process was blocked for. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), true /* write lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, fds_[1] /* Socket fd for timing information */, - fl.l_start, fl.l_len, &child_pid)); - - // We will wait kHoldLockForSec before we release our lock allowing the - // subprocess to obtain it. - constexpr absl::Duration kHoldLockFor = absl::Seconds(5); - - const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); - - absl::SleepFor(kHoldLockFor); - - // Unlock our READ lock. - fl.l_type = F_UNLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Read the blocked time from the subprocess socket. - int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); - - // We must have been waiting at least kMinBlockTime. - EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); - - // The FCNTL write lock must always succeed as it will simply block until it - // can obtain the lock. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// This test will veirfy that blocking works as expected when another process -// holds a write lock when obtaining a read lock. This test will hold the lock -// for some amount of time and then wait for the second process to send over the -// socket_fd the amount of time it was blocked for before the lock succeeded. -TEST_F(FcntlLockTest, SetWriteLockThenBlockingReadLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the write lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. This will immediately block - // so we will release our lock after some amount of time and then assert the - // amount of time the other process was blocked for. - pid_t child_pid = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* read lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, fds_[1] /* Socket fd for timing information */, - fl.l_start, fl.l_len, &child_pid)); - - // We will wait kHoldLockForSec before we release our lock allowing the - // subprocess to obtain it. - constexpr absl::Duration kHoldLockFor = absl::Seconds(5); - - const int64_t kMinBlockTimeUsec = absl::ToInt64Microseconds(absl::Seconds(1)); - - absl::SleepFor(kHoldLockFor); - - // Unlock our write lock. - fl.l_type = F_UNLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Read the blocked time from the subprocess socket. - int64_t subprocess_blocked_time_usec = GetSubprocessFcntlTimeInUsec(); - - // We must have been waiting at least kMinBlockTime. - EXPECT_GT(subprocess_blocked_time_usec, kMinBlockTimeUsec); - - // The FCNTL read lock must always succeed as it will simply block until it - // can obtain the lock. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// This test will verify that when one process only holds a read lock that -// another will not block while obtaining a read lock when F_SETLKW is used. -TEST_F(FcntlLockTest, SetReadLockThenBlockingReadLock) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 0; - - // Take the READ lock. - ASSERT_THAT(fcntl(fd.get(), F_SETLKW, &fl), SyscallSucceeds()); - - // Attempt to take the read lock in a sub process. Since multiple processes - // can hold a read lock this should immediately return without blocking - // even though we used F_SETLKW in the subprocess. - pid_t child_pid = 0; - auto sp = ASSERT_NO_ERRNO_AND_VALUE(SubprocessLock( - file.path(), false /* read lock */, true /* Blocking Lock */, - true /* Retry on EINTR */, -1 /* No fd, should not block */, fl.l_start, - fl.l_len, &child_pid)); - - // We never release the lock and the subprocess should still obtain it without - // blocking for any period of time. - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -TEST(FcntlTest, GetO_ASYNC) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int flag_fl = -1; - ASSERT_THAT(flag_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(flag_fl & O_ASYNC, 0); - - int flag_fd = -1; - ASSERT_THAT(flag_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - EXPECT_EQ(flag_fd & O_ASYNC, 0); -} - -TEST(FcntlTest, SetFlO_ASYNC) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before_fl = -1; - ASSERT_THAT(before_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - - int before_fd = -1; - ASSERT_THAT(before_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s.get(), F_SETFL, before_fl | O_ASYNC), SyscallSucceeds()); - - int after_fl = -1; - ASSERT_THAT(after_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after_fl, before_fl | O_ASYNC); - - int after_fd = -1; - ASSERT_THAT(after_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - EXPECT_EQ(after_fd, before_fd); -} - -TEST(FcntlTest, SetFdO_ASYNC) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before_fl = -1; - ASSERT_THAT(before_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - - int before_fd = -1; - ASSERT_THAT(before_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s.get(), F_SETFD, before_fd | O_ASYNC), SyscallSucceeds()); - - int after_fl = -1; - ASSERT_THAT(after_fl = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after_fl, before_fl); - - int after_fd = -1; - ASSERT_THAT(after_fd = fcntl(s.get(), F_GETFD), SyscallSucceeds()); - EXPECT_EQ(after_fd, before_fd); -} - -TEST(FcntlTest, DupAfterO_ASYNC) { - FileDescriptor s1 = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before = -1; - ASSERT_THAT(before = fcntl(s1.get(), F_GETFL), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s1.get(), F_SETFL, before | O_ASYNC), SyscallSucceeds()); - - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(s1.Dup()); - - int after = -1; - ASSERT_THAT(after = fcntl(fd2.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after & O_ASYNC, O_ASYNC); -} - -TEST(FcntlTest, GetOwnNone) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Use the raw syscall because the glibc wrapper may convert F_{GET,SET}OWN - // into F_{GET,SET}OWN_EX. - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, GetOwnExNone) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetOwnInvalidPid) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, 12345678), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnInvalidPgrp) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, -12345678), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - pid_t pid; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, pid), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(pid)); -} - -TEST(FcntlTest, SetOwnPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - pid_t pgid; - EXPECT_THAT(pgid = getpgrp(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, -pgid), - SyscallSucceedsWithValue(0)); - - // Verify with F_GETOWN_EX; using F_GETOWN on Linux may incorrectly treat the - // negative return value as an error, converting the return value to -1 and - // setting errno accordingly. - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, F_OWNER_PGRP); - EXPECT_EQ(got_owner.pid, pgid); -} - -TEST(FcntlTest, SetOwnUnset) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Set and unset pid. - pid_t pid; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, pid), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, 0), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); - - // Set and unset pgid. - pid_t pgid; - EXPECT_THAT(pgid = getpgrp(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, -pgid), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, 0), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); -} - -// F_SETOWN flips the sign of negative values, an operation that is guarded -// against overflow. -TEST(FcntlTest, SetOwnOverflow) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, INT_MIN), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(FcntlTest, SetOwnExInvalidType) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = __pid_type(-1); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(FcntlTest, SetOwnExInvalidTid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_TID; - owner.pid = -1; - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnExInvalidPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_PID; - owner.pid = -1; - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnExInvalidPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_PGRP; - owner.pid = -1; - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(FcntlTest, SetOwnExTid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_TID; - EXPECT_THAT(owner.pid = syscall(__NR_gettid), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(owner.pid)); -} - -TEST(FcntlTest, SetOwnExPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex owner = {}; - owner.type = F_OWNER_PID; - EXPECT_THAT(owner.pid = getpid(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(owner.pid)); -} - -TEST(FcntlTest, SetOwnExPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_PGRP; - EXPECT_THAT(set_owner.pid = getpgrp(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - // Verify with F_GETOWN_EX; using F_GETOWN on Linux may incorrectly treat the - // negative return value as an error, converting the return value to -1 and - // setting errno accordingly. - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, SetOwnExUnset) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Set and unset pid. - f_owner_ex owner = {}; - owner.type = F_OWNER_PID; - EXPECT_THAT(owner.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - owner.pid = 0; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); - - // Set and unset pgid. - owner.type = F_OWNER_PGRP; - EXPECT_THAT(owner.pid = getpgrp(), SyscallSucceeds()); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - owner.pid = 0; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &owner), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, GetOwnExTid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_TID; - EXPECT_THAT(set_owner.pid = syscall(__NR_gettid), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, GetOwnExPid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_PID; - EXPECT_THAT(set_owner.pid = getpid(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, GetOwnExPgrp) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - f_owner_ex set_owner = {}; - set_owner.type = F_OWNER_PGRP; - EXPECT_THAT(set_owner.pid = getpgrp(), SyscallSucceeds()); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN_EX, &set_owner), - SyscallSucceedsWithValue(0)); - - f_owner_ex got_owner = {}; - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETOWN_EX, &got_owner), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(got_owner.type, set_owner.type); - EXPECT_EQ(got_owner.pid, set_owner.pid); -} - -TEST(FcntlTest, SetSig) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGUSR1), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(SIGUSR1)); -} - -TEST(FcntlTest, SetSigDefaultsToZero) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - // Defaults to returning the zero value, indicating default behavior (SIGIO). - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetSigToDefault) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGIO), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(SIGIO)); - - // Can be reset to the default behavior. - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, 0), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetSigInvalid) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGRTMAX + 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(0)); -} - -TEST(FcntlTest, SetSigInvalidDoesNotResetPreviousChoice) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGUSR1), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGRTMAX + 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(syscall(__NR_fcntl, s.get(), F_GETSIG), - SyscallSucceedsWithValue(SIGUSR1)); -} - -TEST_F(FcntlSignalTest, SetSigDefault) { - const auto signal_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - RegisterFD(pipe_read_fd_, 0); // Zero = default behavior - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - signals_received_.pop_front(); - EXPECT_EQ(sig.num, SIGIO); - EXPECT_EQ(sig.info.si_signo, SIGIO); - // siginfo contents is undefined in this case. -} - -TEST_F(FcntlSignalTest, SetSigCustom) { - const auto signal_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - signals_received_.pop_front(); - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigUnregisterStillGetsSigio) { - const auto sigio_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - RegisterFD(pipe_read_fd_, 0); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - signals_received_.pop_front(); - EXPECT_EQ(sig.num, SIGIO); - // siginfo contents is undefined in this case. -} - -TEST_F(FcntlSignalTest, SetSigWithSigioStillGetsSiginfo) { - const auto signal_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - RegisterFD(pipe_read_fd_, SIGIO); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - EXPECT_EQ(sig.num, SIGIO); - EXPECT_EQ(sig.info.si_signo, SIGIO); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupThenCloseOld) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - FlushAndCloseFD(pipe_read_fd_); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the **old** FD (even though it is closed). - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupThenCloseNew) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - FlushAndCloseFD(pipe_read_fd_dup_); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the old FD. - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupOldRegistered) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the old FD. - EXPECT_EQ(sig.num, SIGUSR1); - EXPECT_EQ(sig.info.si_signo, SIGUSR1); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupNewRegistered) { - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the new FD. - EXPECT_EQ(sig.num, SIGUSR2); - EXPECT_EQ(sig.info.si_signo, SIGUSR2); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_dup_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupBothRegistered) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the **new** signal number, but the **old** FD. - EXPECT_EQ(sig.num, SIGUSR2); - EXPECT_EQ(sig.info.si_signo, SIGUSR2); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupBothRegisteredAfterDup) { - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - DupReadFD(); - RegisterFD(pipe_read_fd_, SIGUSR1); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with the **new** signal number, but the **old** FD. - EXPECT_EQ(sig.num, SIGUSR2); - EXPECT_EQ(sig.info.si_signo, SIGUSR2); - EXPECT_EQ(sig.info.si_fd, pipe_read_fd_); - EXPECT_EQ(sig.info.si_band, EPOLLIN | EPOLLRDNORM); -} - -TEST_F(FcntlSignalTest, SetSigDupUnregisterOld) { - const auto sigio_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - RegisterFD(pipe_read_fd_, 0); // Should go back to SIGIO behavior. - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with SIGIO. - EXPECT_EQ(sig.num, SIGIO); - // siginfo is undefined in this case. -} - -TEST_F(FcntlSignalTest, SetSigDupUnregisterNew) { - const auto sigio_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGIO)); - const auto sigusr1_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR1)); - const auto sigusr2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(RegisterSignalHandler(SIGUSR2)); - RegisterFD(pipe_read_fd_, SIGUSR1); - DupReadFD(); - RegisterFD(pipe_read_fd_dup_, SIGUSR2); - RegisterFD(pipe_read_fd_dup_, 0); // Should go back to SIGIO behavior. - GenerateIOEvent(); - WaitForSignalDelivery(absl::Seconds(1)); - ASSERT_EQ(num_signals_received_, 1); - SignalDelivery sig = signals_received_.front(); - // We get a signal with SIGIO. - EXPECT_EQ(sig.num, SIGIO); - // siginfo is undefined in this case. -} - -// Make sure that making multiple concurrent changes to async signal generation -// does not cause any race issues. -TEST(FcntlTest, SetFlSetOwnSetSigDoNotRace) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - pid_t pid; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - - constexpr absl::Duration runtime = absl::Milliseconds(300); - auto set_async = [&s, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETFL, O_ASYNC), - SyscallSucceeds()); - sched_yield(); - } - }; - auto reset_async = [&s, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETFL, 0), SyscallSucceeds()); - sched_yield(); - } - }; - auto set_own = [&s, &pid, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETOWN, pid), - SyscallSucceeds()); - sched_yield(); - } - }; - auto set_sig = [&s, &runtime] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(syscall(__NR_fcntl, s.get(), F_SETSIG, SIGUSR1), - SyscallSucceeds()); - sched_yield(); - } - }; - - std::list<ScopedThread> threads; - for (int i = 0; i < 10; i++) { - threads.emplace_back(set_async); - threads.emplace_back(reset_async); - threads.emplace_back(set_own); - threads.emplace_back(set_sig); - } -} - -TEST_F(FcntlLockTest, GetLockOnNothing) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); - ASSERT_TRUE(fl.l_type == F_UNLCK); -} - -TEST_F(FcntlLockTest, GetLockOnLockSameProcess) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); - ASSERT_TRUE(fl.l_type == F_UNLCK); - - fl.l_type = F_WRLCK; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); - ASSERT_TRUE(fl.l_type == F_UNLCK); -} - -TEST_F(FcntlLockTest, GetReadLockOnReadLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_UNLCK); - _exit(0); - } - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -TEST_F(FcntlLockTest, GetReadLockOnWriteLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - fl.l_type = F_RDLCK; - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_WRLCK); - TEST_CHECK(fl.l_pid == getppid()); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -TEST_F(FcntlLockTest, GetWriteLockOnReadLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_RDLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - fl.l_type = F_WRLCK; - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_RDLCK); - TEST_CHECK(fl.l_pid == getppid()); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -TEST_F(FcntlLockTest, GetWriteLockOnWriteLock) { - SKIP_IF(IsRunningWithVFS1()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_WRLCK); - TEST_CHECK(fl.l_pid == getppid()); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -// Tests that the pid returned from F_GETLK is relative to the caller's PID -// namespace. -TEST_F(FcntlLockTest, GetLockRespectsPIDNamespace) { - SKIP_IF(IsRunningWithVFS1()); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - std::string filename = file.path(); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR, 0666)); - - // Lock in the parent process. - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); - - auto child_getlk = [](void* filename) { - int fd = open((char*)filename, O_RDWR, 0666); - TEST_CHECK(fd >= 0); - - struct flock fl; - fl.l_type = F_WRLCK; - fl.l_whence = SEEK_SET; - fl.l_start = 0; - fl.l_len = 40; - TEST_CHECK(fcntl(fd, F_GETLK, &fl) >= 0); - TEST_CHECK(fl.l_type == F_WRLCK); - // Parent PID should be 0 in the child PID namespace. - TEST_CHECK(fl.l_pid == 0); - close(fd); - return 0; - }; - - // Set up child process in a new PID namespace. - constexpr int kStackSize = 4096; - Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); - pid_t child_pid; - ASSERT_THAT( - child_pid = clone(child_getlk, (char*)stack.ptr() + stack.len(), - CLONE_NEWPID | SIGCHLD, (void*)filename.c_str()), - SyscallSucceeds()); - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int set_lock() { - const std::string set_lock_on = absl::GetFlag(FLAGS_child_set_lock_on); - int socket_fd = absl::GetFlag(FLAGS_socket_fd); - int fd = open(set_lock_on.c_str(), O_RDWR, 0666); - if (fd == -1 && errno != 0) { - int err = errno; - std::cerr << "CHILD open " << set_lock_on << " failed: " << err - << std::endl; - return err; - } - - struct flock fl; - if (absl::GetFlag(FLAGS_child_set_lock_write)) { - fl.l_type = F_WRLCK; - } else { - fl.l_type = F_RDLCK; - } - fl.l_whence = SEEK_SET; - fl.l_start = absl::GetFlag(FLAGS_child_set_lock_start); - fl.l_len = absl::GetFlag(FLAGS_child_set_lock_len); - - // Test the fcntl. - int err = 0; - int ret = 0; - - gvisor::testing::MonotonicTimer timer; - timer.Start(); - do { - ret = fcntl(fd, absl::GetFlag(FLAGS_blocking) ? F_SETLKW : F_SETLK, &fl); - } while (absl::GetFlag(FLAGS_retry_eintr) && ret == -1 && errno == EINTR); - auto usec = absl::ToInt64Microseconds(timer.Duration()); - - if (ret == -1 && errno != 0) { - err = errno; - std::cerr << "CHILD lock " << set_lock_on << " failed " << err << std::endl; - } - - // If there is a socket fd let's send back the time in microseconds it took - // to execute this syscall. - if (socket_fd != -1) { - gvisor::testing::WriteFd(socket_fd, reinterpret_cast<void*>(&usec), - sizeof(usec)); - close(socket_fd); - } - - close(fd); - return err; -} - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (!absl::GetFlag(FLAGS_child_set_lock_on).empty()) { - exit(set_lock()); - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/file_base.h b/test/syscalls/linux/file_base.h deleted file mode 100644 index fb418e052..000000000 --- a/test/syscalls/linux/file_base.h +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_FILE_BASE_H_ -#define GVISOR_TEST_SYSCALLS_FILE_BASE_H_ - -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <netinet/in.h> -#include <stddef.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> - -#include <cstring> -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -class FileTest : public ::testing::Test { - public: - void SetUp() override { - test_pipe_[0] = -1; - test_pipe_[1] = -1; - - test_file_name_ = NewTempAbsPath(); - test_file_fd_ = ASSERT_NO_ERRNO_AND_VALUE( - Open(test_file_name_, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)); - - ASSERT_THAT(pipe(test_pipe_), SyscallSucceeds()); - ASSERT_THAT(fcntl(test_pipe_[0], F_SETFL, O_NONBLOCK), SyscallSucceeds()); - } - - // CloseFile will allow the test to manually close the file descriptor. - void CloseFile() { test_file_fd_.reset(); } - - // UnlinkFile will allow the test to manually unlink the file. - void UnlinkFile() { - if (!test_file_name_.empty()) { - EXPECT_THAT(unlink(test_file_name_.c_str()), SyscallSucceeds()); - test_file_name_.clear(); - } - } - - // ClosePipes will allow the test to manually close the pipes. - void ClosePipes() { - if (test_pipe_[0] > 0) { - EXPECT_THAT(close(test_pipe_[0]), SyscallSucceeds()); - } - - if (test_pipe_[1] > 0) { - EXPECT_THAT(close(test_pipe_[1]), SyscallSucceeds()); - } - - test_pipe_[0] = -1; - test_pipe_[1] = -1; - } - - void TearDown() override { - CloseFile(); - UnlinkFile(); - ClosePipes(); - } - - protected: - std::string test_file_name_; - FileDescriptor test_file_fd_; - - int test_pipe_[2]; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_FILE_BASE_H_ diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc deleted file mode 100644 index b286e84fe..000000000 --- a/test/syscalls/linux/flock.cc +++ /dev/null @@ -1,716 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sys/file.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/file_base.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class FlockTest : public FileTest {}; - -TEST_F(FlockTest, InvalidOpCombinations) { - // The operation cannot be both exclusive and shared. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_SH | LOCK_NB), - SyscallFailsWithErrno(EINVAL)); - - // Locking and Unlocking doesn't make sense. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_UN | LOCK_NB), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_UN | LOCK_NB), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(FlockTest, NoOperationSpecified) { - // Not specifying an operation is invalid. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(FlockTest, TestSimpleExLock) { - // Test that we can obtain an exclusive lock (no other holders) - // and that we can unlock it. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSimpleShLock) { - // Test that we can obtain a shared lock (no other holders) - // and that we can unlock it. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestLockableAnyMode) { - // flock(2): A shared or exclusive lock can be placed on a file - // regardless of the mode in which the file was opened. - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(test_file_name_, O_RDONLY)); // open read only to test - - // Mode shouldn't prevent us from taking an exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestUnlockWithNoHolders) { - // Test that unlocking when no one holds a lock succeeeds. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestRepeatedExLockingBySameHolder) { - // Test that repeated locking by the same holder for the - // same type of lock works correctly. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestRepeatedExLockingSingleUnlock) { - // Test that repeated locking by the same holder for the - // same type of lock works correctly and that a single unlock is required. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - - // Should be unlocked at this point - ASSERT_THAT(flock(fd.get(), LOCK_NB | LOCK_EX), SyscallSucceedsWithValue(0)); - - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestRepeatedShLockingBySameHolder) { - // Test that repeated locking by the same holder for the - // same type of lock works correctly. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSingleHolderUpgrade) { - // Test that a shared lock is upgradable when no one else holds a lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSingleHolderDowngrade) { - // Test single holder lock downgrade case. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestMultipleShared) { - // This is a simple test to verify that multiple independent shared - // locks will be granted. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // A shared lock should be granted as there only exists other shared locks. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock both. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -/* - * flock(2): If a process uses open(2) (or similar) to obtain more than one - * descriptor for the same file, these descriptors are treated - * independently by flock(). An attempt to lock the file using one of - * these file descriptors may be denied by a lock that the calling process - * has already placed via another descriptor. - */ -TEST_F(FlockTest, TestMultipleHolderSharedExclusive) { - // This test will verify that an exclusive lock will not be granted - // while a shared is held. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify We're unable to get an exlcusive lock via the second FD. - // because someone is holding a shared lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestSharedLockFailExclusiveHolderNonblocking) { - // This test will verify that a shared lock is denied while - // someone holds an exclusive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify we're unable to get an shared lock via the second FD. - // because someone is holding an exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -void trivial_handler(int signum) {} - -TEST_F(FlockTest, TestSharedLockFailExclusiveHolderBlocking_NoRandomSave) { - const DisableSave ds; // Timing-related. - - // This test will verify that a shared lock is denied while - // someone holds an exclusive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Make sure that a blocking flock() call will return EINTR when interrupted - // by a signal. Create a timer that will go off while blocking on flock(), and - // register the corresponding signal handler. - auto timer = ASSERT_NO_ERRNO_AND_VALUE( - TimerCreate(CLOCK_MONOTONIC, sigevent_t{ - .sigev_signo = SIGALRM, - .sigev_notify = SIGEV_SIGNAL, - })); - - struct sigaction act = {}; - act.sa_handler = trivial_handler; - ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds()); - - // Now that the signal handler is registered, set the timer. Set an interval - // so that it's ok if the timer goes off before we call flock. - ASSERT_NO_ERRNO( - timer.Set(0, itimerspec{ - .it_interval = absl::ToTimespec(absl::Milliseconds(10)), - .it_value = absl::ToTimespec(absl::Milliseconds(10)), - })); - - ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallFailsWithErrno(EINTR)); - timer.reset(); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderNonblocking) { - // This test will verify that an exclusive lock is denied while - // someone already holds an exclsuive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify we're unable to get an exclusive lock via the second FD - // because someone is already holding an exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderBlocking_NoRandomSave) { - const DisableSave ds; // Timing-related. - - // This test will verify that an exclusive lock is denied while - // someone already holds an exclsuive lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Make sure that a blocking flock() call will return EINTR when interrupted - // by a signal. Create a timer that will go off while blocking on flock(), and - // register the corresponding signal handler. - auto timer = ASSERT_NO_ERRNO_AND_VALUE( - TimerCreate(CLOCK_MONOTONIC, sigevent_t{ - .sigev_signo = SIGALRM, - .sigev_notify = SIGEV_SIGNAL, - })); - - struct sigaction act = {}; - act.sa_handler = trivial_handler; - ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds()); - - // Now that the signal handler is registered, set the timer. Set an interval - // so that it's ok if the timer goes off before we call flock. - ASSERT_NO_ERRNO( - timer.Set(0, itimerspec{ - .it_interval = absl::ToTimespec(absl::Milliseconds(10)), - .it_value = absl::ToTimespec(absl::Milliseconds(10)), - })); - - ASSERT_THAT(flock(fd.get(), LOCK_EX), SyscallFailsWithErrno(EINTR)); - timer.reset(); - - // Unlock - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestMultipleHolderSharedExclusiveUpgrade) { - // This test will verify that we cannot obtain an exclusive lock while - // a shared lock is held by another descriptor, then verify that an upgrade - // is possible on a shared lock once all other shared locks have closed. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify we're unable to get an exclusive lock via the second FD because - // a shared lock is held. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Verify that we can get a shared lock via the second descriptor instead - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock the first and there will only be one shared lock remaining. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - - // Upgrade 2nd fd. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Finally unlock the second - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestMultipleHolderSharedExclusiveDowngrade) { - // This test will verify that a shared lock is not obtainable while an - // exclusive lock is held but that once the first is downgraded that - // the second independent file descriptor can also get a shared lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Verify We're unable to get a shared lock via the second FD because - // an exclusive lock is held. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Verify that we can downgrade the first. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - // Now verify that we can obtain a shared lock since the first was downgraded. - ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Finally unlock both. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -/* - * flock(2): Locks created by flock() are associated with an open file table - * entry. This means that duplicate file descriptors (created by, for example, - * fork(2) or dup(2)) refer to the same lock, and this lock may be modified or - * released using any of these descriptors. Furthermore, the lock is released - * either by an explicit LOCK_UN operation on any of these duplicate descriptors - * or when all such descriptors have been closed. - */ -TEST_F(FlockTest, TestDupFdUpgrade) { - // This test will verify that a shared lock is upgradeable via a dupped - // file descriptor, if the FD wasn't dupped this would fail. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // Now we should be able to upgrade via the dupped fd. - ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - // Validate unlock via dupped fd. - ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdDowngrade) { - // This test will verify that a exclusive lock is downgradable via a dupped - // file descriptor, if the FD wasn't dupped this would fail. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // Now we should be able to downgrade via the dupped fd. - ASSERT_THAT(flock(dup_fd.get(), LOCK_SH | LOCK_NB), - SyscallSucceedsWithValue(0)); - - // Validate unlock via dupped fd - ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdCloseRelease) { - // flock(2): Furthermore, the lock is released either by an explicit LOCK_UN - // operation on any of these duplicate descriptors, or when all such - // descriptors have been closed. - // - // This test will verify that a dupped fd closing will not release the - // underlying lock until all such dupped fds have closed. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // At this point we have ONE exclusive locked referenced by two different fds. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Validate that we cannot get a lock on a new unrelated FD. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Closing the dupped fd shouldn't affect the lock until all are closed. - dup_fd.reset(); // Closed the duped fd. - - // Validate that we still cannot get a lock on a new unrelated FD. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Closing the first fd - CloseFile(); // Will validate the syscall succeeds. - - // Now we should actually be able to get a lock since all fds related to - // the first lock are closed. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdUnlockRelease) { - /* flock(2): Furthermore, the lock is released either by an explicit LOCK_UN - * operation on any of these duplicate descriptors, or when all such - * descriptors have been closed. - */ - // This test will verify that an explict unlock on a dupped FD will release - // the underlying lock unlike the previous case where close on a dup was - // not enough to release the lock. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB), - SyscallSucceedsWithValue(0)); - - const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // At this point we have ONE exclusive locked referenced by two different fds. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Validate that we cannot get a lock on a new unrelated FD. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Explicitly unlock via the dupped descriptor. - ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); - - // Validate that we can now get the lock since we explicitly unlocked. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0)); - - // Unlock - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -TEST_F(FlockTest, TestDupFdFollowedByLock) { - // This test will verify that taking a lock on a file descriptor that has - // already been dupped means that the lock is shared between both. This is - // slightly different than than duping on an already locked FD. - FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup()); - - // Take a lock. - ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); - - // Now dup_fd and test_file_ should both reference the same lock. - // We shouldn't be able to obtain a lock until both are closed. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Closing the first fd - dup_fd.reset(); // Close the duped fd. - - // Validate that we cannot get a lock yet because the dupped descriptor. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Closing the second fd. - CloseFile(); // CloseFile() will validate the syscall succeeds. - - // Now we should be able to get the lock. - ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); - - // Unlock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0)); -} - -// NOTE: These blocking tests are not perfect. Unfortunately it's very hard to -// determine if a thread was actually blocked in the kernel so we're forced -// to use timing. -TEST_F(FlockTest, BlockingLockNoBlockingForSharedLocks_NoRandomSave) { - // This test will verify that although LOCK_NB isn't specified - // two different fds can obtain shared locks without blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(30); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // Only a single shared lock is held, the lock will be granted immediately. - // This should be granted without any blocking. Don't save here to avoid - // wild discrepencies on timing. - timer.Start(); - ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallSucceeds()); - - // We held the lock for 30 seconds but this thread should not have - // blocked at all so we expect a very small duration on syscall completion. - ASSERT_LT(timer.Duration(), - absl::Seconds(1)); // 1000ms is much less than 30s. - - // We can release our second shared lock - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the first shared lock. Don't save in this situation to avoid - // discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST_F(FlockTest, BlockingLockFirstSharedSecondExclusive_NoRandomSave) { - // This test will verify that if someone holds a shared lock any attempt to - // obtain an exclusive lock will result in blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(2); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // This exclusive lock should block because someone is already holding a - // shared lock. We don't save here to avoid wild discrepencies on timing. - timer.Start(); - ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds()); - - // We should be blocked, we will expect to be blocked for more than 1.0s. - ASSERT_GT(timer.Duration(), absl::Seconds(1)); - - // We can release our exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the shared lock allowing the thread to proceed. - // We don't save here to avoid wild discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST_F(FlockTest, BlockingLockFirstExclusiveSecondShared_NoRandomSave) { - // This test will verify that if someone holds an exclusive lock any attempt - // to obtain a shared lock will result in blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(2); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // This shared lock should block because someone is already holding an - // exclusive lock. We don't save here to avoid wild discrepencies on timing. - timer.Start(); - ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_SH), SyscallSucceeds()); - - // We should be blocked, we will expect to be blocked for more than 1.0s. - ASSERT_GT(timer.Duration(), absl::Seconds(1)); - - // We can release our shared lock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the exclusive lock allowing the blocked thread to proceed. - // We don't save here to avoid wild discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST_F(FlockTest, BlockingLockFirstExclusiveSecondExclusive_NoRandomSave) { - // This test will verify that if someone holds an exclusive lock any attempt - // to obtain another exclusive lock will result in blocking. - ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds()); - - // kHoldLockTime is the amount of time we will hold the lock before releasing. - constexpr absl::Duration kHoldLockTime = absl::Seconds(2); - - const DisableSave ds; // Timing-related. - - // We do this in another thread so we can determine if it was actually - // blocked by timing the amount of time it took for the syscall to complete. - ScopedThread t([&] { - MonotonicTimer timer; - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - // This exclusive lock should block because someone is already holding an - // exclusive lock. - timer.Start(); - ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds()); - - // We should be blocked, we will expect to be blocked for more than 1.0s. - ASSERT_GT(timer.Duration(), absl::Seconds(1)); - - // We can release our exclusive lock. - ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds()); - }); - - // Sleep before unlocking. - absl::SleepFor(kHoldLockTime); - - // Release the exclusive lock allowing the blocked thread to proceed. - // We don't save to avoid wild discrepencies in timing. - EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, BadFD) { - // EBADF: fd is not an open file descriptor. - ASSERT_THAT(flock(-1, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST(FlockTestNoFixture, FlockDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0000)); - EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, FlockSymlink) { - // TODO(gvisor.dev/issue/2782): Replace with IsRunningWithVFS1() when O_PATH - // is supported. - SKIP_IF(IsRunningOnGvisor()); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto symlink = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), file.path())); - - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(symlink.path(), O_RDONLY | O_PATH, 0000)); - EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallFailsWithErrno(EBADF)); -} - -TEST(FlockTestNoFixture, FlockProc) { - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/status", O_RDONLY, 0000)); - EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, FlockPipe) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - EXPECT_THAT(flock(fds[0], LOCK_EX | LOCK_NB), SyscallSucceeds()); - // Check that the pipe was locked above. - EXPECT_THAT(flock(fds[1], LOCK_EX | LOCK_NB), SyscallFailsWithErrno(EAGAIN)); - - EXPECT_THAT(flock(fds[0], LOCK_UN), SyscallSucceeds()); - EXPECT_THAT(flock(fds[1], LOCK_EX | LOCK_NB), SyscallSucceeds()); - - EXPECT_THAT(close(fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds[1]), SyscallSucceeds()); -} - -TEST(FlockTestNoFixture, FlockSocket) { - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - ASSERT_THAT(sock, SyscallSucceeds()); - - struct sockaddr_un addr = - ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(true /* abstract */, AF_UNIX)); - ASSERT_THAT( - bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceeds()); - - EXPECT_THAT(flock(sock, LOCK_EX | LOCK_NB), SyscallSucceeds()); - EXPECT_THAT(close(sock), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fork.cc b/test/syscalls/linux/fork.cc deleted file mode 100644 index 853f6231a..000000000 --- a/test/syscalls/linux/fork.cc +++ /dev/null @@ -1,464 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <sched.h> -#include <stdlib.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <atomic> -#include <cstdlib> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::Ge; - -class ForkTest : public ::testing::Test { - protected: - // SetUp creates a populated, open file. - void SetUp() override { - // Make a shared mapping. - shared_ = reinterpret_cast<char*>(mmap(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0)); - ASSERT_NE(reinterpret_cast<void*>(shared_), MAP_FAILED); - - // Make a private mapping. - private_ = - reinterpret_cast<char*>(mmap(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); - ASSERT_NE(reinterpret_cast<void*>(private_), MAP_FAILED); - - // Make a pipe. - ASSERT_THAT(pipe(pipes_), SyscallSucceeds()); - } - - // TearDown frees associated resources. - void TearDown() override { - EXPECT_THAT(munmap(shared_, kPageSize), SyscallSucceeds()); - EXPECT_THAT(munmap(private_, kPageSize), SyscallSucceeds()); - EXPECT_THAT(close(pipes_[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipes_[1]), SyscallSucceeds()); - } - - // Fork executes a clone system call. - pid_t Fork() { - pid_t pid = fork(); - MaybeSave(); - TEST_PCHECK_MSG(pid >= 0, "fork failed"); - return pid; - } - - // Wait waits for the given pid and returns the exit status. If the child was - // killed by a signal or an error occurs, then 256+signal is returned. - int Wait(pid_t pid) { - int status; - while (true) { - int rval = wait4(pid, &status, 0, NULL); - if (rval < 0) { - return rval; - } - if (rval != pid) { - continue; - } - if (WIFEXITED(status)) { - return WEXITSTATUS(status); - } - if (WIFSIGNALED(status)) { - return 256 + WTERMSIG(status); - } - } - } - - // Exit exits the proccess. - void Exit(int code) { - _exit(code); - - // Should never reach here. Since the exit above failed, we really don't - // have much in the way of options to indicate failure. So we just try to - // log an assertion failure to the logs. The parent process will likely - // fail anyways if exit is not working. - TEST_CHECK_MSG(false, "_exit returned"); - } - - // ReadByte reads a byte from the shared pipe. - char ReadByte() { - char val = -1; - TEST_PCHECK(ReadFd(pipes_[0], &val, 1) == 1); - MaybeSave(); - return val; - } - - // WriteByte writes a byte from the shared pipe. - void WriteByte(char val) { - TEST_PCHECK(WriteFd(pipes_[1], &val, 1) == 1); - MaybeSave(); - } - - // Shared pipe. - int pipes_[2]; - - // Shared mapping (one page). - char* shared_; - - // Private mapping (one page). - char* private_; -}; - -TEST_F(ForkTest, Simple) { - pid_t child = Fork(); - if (child == 0) { - Exit(0); - } - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -TEST_F(ForkTest, ExitCode) { - pid_t child = Fork(); - if (child == 0) { - Exit(123); - } - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(123)); - child = Fork(); - if (child == 0) { - Exit(1); - } - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(1)); -} - -TEST_F(ForkTest, Multi) { - pid_t child1 = Fork(); - if (child1 == 0) { - Exit(0); - } - pid_t child2 = Fork(); - if (child2 == 0) { - Exit(1); - } - EXPECT_THAT(Wait(child1), SyscallSucceedsWithValue(0)); - EXPECT_THAT(Wait(child2), SyscallSucceedsWithValue(1)); -} - -TEST_F(ForkTest, Pipe) { - pid_t child = Fork(); - if (child == 0) { - WriteByte(1); - Exit(0); - } - EXPECT_EQ(ReadByte(), 1); - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -TEST_F(ForkTest, SharedMapping) { - pid_t child = Fork(); - if (child == 0) { - // Wait for the parent. - ReadByte(); - if (shared_[0] == 1) { - Exit(0); - } - // Failed. - Exit(1); - } - // Change the mapping. - ASSERT_EQ(shared_[0], 0); - shared_[0] = 1; - // Unblock the child. - WriteByte(0); - // Did it work? - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -TEST_F(ForkTest, PrivateMapping) { - pid_t child = Fork(); - if (child == 0) { - // Wait for the parent. - ReadByte(); - if (private_[0] == 0) { - Exit(0); - } - // Failed. - Exit(1); - } - // Change the mapping. - ASSERT_EQ(private_[0], 0); - private_[0] = 1; - // Unblock the child. - WriteByte(0); - // Did it work? - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -// CPUID is x86 specific. -#ifdef __x86_64__ -// Test that cpuid works after a fork. -TEST_F(ForkTest, Cpuid) { - pid_t child = Fork(); - - // We should be able to determine the CPU vendor. - ASSERT_NE(GetCPUVendor(), CPUVendor::kUnknownVendor); - - if (child == 0) { - Exit(0); - } - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} -#endif - -TEST_F(ForkTest, Mmap) { - pid_t child = Fork(); - - if (child == 0) { - void* addr = - mmap(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - MaybeSave(); - Exit(addr == MAP_FAILED); - } - - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -static volatile int alarmed = 0; - -void AlarmHandler(int sig, siginfo_t* info, void* context) { alarmed = 1; } - -TEST_F(ForkTest, Alarm) { - // Setup an alarm handler. - struct sigaction sa; - sa.sa_sigaction = AlarmHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - EXPECT_THAT(sigaction(SIGALRM, &sa, nullptr), SyscallSucceeds()); - - pid_t child = Fork(); - - if (child == 0) { - alarm(1); - sleep(3); - if (!alarmed) { - Exit(1); - } - Exit(0); - } - - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, alarmed); -} - -// Child cannot affect parent private memory. Regression test for b/24137240. -TEST_F(ForkTest, PrivateMemory) { - std::atomic<uint32_t> local(0); - - pid_t child1 = Fork(); - if (child1 == 0) { - local++; - - pid_t child2 = Fork(); - if (child2 == 0) { - local++; - - TEST_CHECK(local.load() == 2); - - Exit(0); - } - - TEST_PCHECK(Wait(child2) == 0); - TEST_CHECK(local.load() == 1); - Exit(0); - } - - EXPECT_THAT(Wait(child1), SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, local.load()); -} - -// Kernel-accessed buffers should remain coherent across COW. -// -// The buffer must be >= usermem.ZeroCopyMinBytes, as UnsafeAccess operates -// differently. Regression test for b/33811887. -TEST_F(ForkTest, COWSegment) { - constexpr int kBufSize = 1024; - char* read_buf = private_; - char* touch = private_ + kPageSize / 2; - - std::string contents(kBufSize, 'a'); - - ScopedThread t([&] { - // Wait to be sure the parent is blocked in read. - absl::SleepFor(absl::Seconds(3)); - - // Fork to mark private pages for COW. - // - // Use fork directly rather than the Fork wrapper to skip the multi-threaded - // check, and limit the child to async-signal-safe functions: - // - // "After a fork() in a multithreaded program, the child can safely call - // only async-signal-safe functions (see signal(7)) until such time as it - // calls execve(2)." - // - // Skip ASSERT in the child, as it isn't async-signal-safe. - pid_t child = fork(); - if (child == 0) { - // Wait to be sure parent touched memory. - sleep(3); - Exit(0); - } - - // Check success only in the parent. - ASSERT_THAT(child, SyscallSucceedsWithValue(Ge(0))); - - // Trigger COW on private page. - *touch = 42; - - // Write to pipe. Parent should still be able to read this. - EXPECT_THAT(WriteFd(pipes_[1], contents.c_str(), kBufSize), - SyscallSucceedsWithValue(kBufSize)); - - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); - }); - - EXPECT_THAT(ReadFd(pipes_[0], read_buf, kBufSize), - SyscallSucceedsWithValue(kBufSize)); - EXPECT_STREQ(contents.c_str(), read_buf); -} - -TEST_F(ForkTest, SigAltStack) { - std::vector<char> stack_mem(SIGSTKSZ); - stack_t stack = {}; - stack.ss_size = SIGSTKSZ; - stack.ss_sp = stack_mem.data(); - ASSERT_THAT(sigaltstack(&stack, nullptr), SyscallSucceeds()); - - pid_t child = Fork(); - - if (child == 0) { - stack_t oss = {}; - TEST_PCHECK(sigaltstack(nullptr, &oss) == 0); - MaybeSave(); - - TEST_CHECK((oss.ss_flags & SS_DISABLE) == 0); - TEST_CHECK(oss.ss_size == SIGSTKSZ); - TEST_CHECK(oss.ss_sp == stack.ss_sp); - - Exit(0); - } - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -TEST_F(ForkTest, Affinity) { - // Make a non-default cpumask. - cpu_set_t parent_mask; - EXPECT_THAT(sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &parent_mask), - SyscallSucceeds()); - // Knock out the lowest bit. - for (unsigned int n = 0; n < CPU_SETSIZE; n++) { - if (CPU_ISSET(n, &parent_mask)) { - CPU_CLR(n, &parent_mask); - break; - } - } - EXPECT_THAT(sched_setaffinity(/*pid=*/0, sizeof(cpu_set_t), &parent_mask), - SyscallSucceeds()); - - pid_t child = Fork(); - if (child == 0) { - cpu_set_t child_mask; - - int ret = sched_getaffinity(/*pid=*/0, sizeof(cpu_set_t), &child_mask); - MaybeSave(); - if (ret < 0) { - Exit(-ret); - } - - TEST_CHECK(CPU_EQUAL(&child_mask, &parent_mask)); - - Exit(0); - } - - EXPECT_THAT(Wait(child), SyscallSucceedsWithValue(0)); -} - -TEST(CloneTest, NewUserNamespacePermitsAllOtherNamespaces) { - // "If CLONE_NEWUSER is specified along with other CLONE_NEW* flags in a - // single clone(2) or unshare(2) call, the user namespace is guaranteed to be - // created first, giving the child (clone(2)) or caller (unshare(2)) - // privileges over the remaining namespaces created by the call. Thus, it is - // possible for an unprivileged caller to specify this combination of flags." - // - user_namespaces(7) - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - Mapping child_stack = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - int child_pid; - // We only test with CLONE_NEWIPC, CLONE_NEWNET, and CLONE_NEWUTS since these - // namespaces were implemented in Linux before user namespaces. - ASSERT_THAT( - child_pid = clone( - +[](void*) { return 0; }, - reinterpret_cast<void*>(child_stack.addr() + kPageSize), - CLONE_NEWUSER | CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWUTS | SIGCHLD, - /* arg = */ nullptr), - SyscallSucceeds()); - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status = " << status; -} - -// Clone with CLONE_SETTLS and a non-canonical TLS address is rejected. -TEST(CloneTest, NonCanonicalTLS) { - constexpr uintptr_t kNonCanonical = 1ull << 48; - - // We need a valid address for the stack pointer. We'll never actually execute - // on this. - char stack; - - // The raw system call interface on x86-64 is: - // long clone(unsigned long flags, void *stack, - // int *parent_tid, int *child_tid, - // unsigned long tls); - // - // While on arm64, the order of the last two arguments is reversed: - // long clone(unsigned long flags, void *stack, - // int *parent_tid, unsigned long tls, - // int *child_tid); -#if defined(__x86_64__) - EXPECT_THAT(syscall(__NR_clone, SIGCHLD | CLONE_SETTLS, &stack, nullptr, - nullptr, kNonCanonical), - SyscallFailsWithErrno(EPERM)); -#elif defined(__aarch64__) - EXPECT_THAT(syscall(__NR_clone, SIGCHLD | CLONE_SETTLS, &stack, nullptr, - kNonCanonical, nullptr), - SyscallFailsWithErrno(EPERM)); -#endif -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc deleted file mode 100644 index c47567b4e..000000000 --- a/test/syscalls/linux/fpsig_fork.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This test verifies that fork(2) in a signal handler will correctly -// restore floating point state after the signal handler returns in both -// the child and parent. -#include <sys/time.h> - -#include "gtest/gtest.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#ifdef __x86_64__ -#define GET_XMM(__var, __xmm) \ - asm volatile("movq %%" #__xmm ", %0" : "=r"(__var)) -#define SET_XMM(__var, __xmm) asm volatile("movq %0, %%" #__xmm : : "r"(__var)) -#define GET_FP0(__var) GET_XMM(__var, xmm0) -#define SET_FP0(__var) SET_XMM(__var, xmm0) -#elif __aarch64__ -#define __stringify_1(x...) #x -#define __stringify(x...) __stringify_1(x) -#define GET_FPREG(var, regname) \ - asm volatile("str " __stringify(regname) ", %0" : "=m"(var)) -#define SET_FPREG(var, regname) \ - asm volatile("ldr " __stringify(regname) ", %0" : "=m"(var)) -#define GET_FP0(var) GET_FPREG(var, d0) -#define SET_FP0(var) SET_FPREG(var, d0) -#endif - -int parent, child; - -void sigusr1(int s, siginfo_t* siginfo, void* _uc) { - // Fork and clobber %xmm0. The fpstate should be restored by sigreturn(2) - // in both parent and child. - child = fork(); - TEST_CHECK_MSG(child >= 0, "fork failed"); - - uint64_t val = SIGUSR1; - SET_FP0(val); - uint64_t got; - GET_FP0(got); - TEST_CHECK_MSG(val == got, "Basic FP check failed in sigusr1()"); -} - -TEST(FPSigTest, Fork) { - parent = getpid(); - pid_t parent_tid = gettid(); - - struct sigaction sa = {}; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = sigusr1; - ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); - - // The amd64 ABI specifies that the XMM register set is caller-saved. This - // implies that if there is any function call between SET_XMM and GET_XMM the - // compiler might save/restore xmm0 implicitly. This defeats the entire - // purpose of the test which is to verify that fpstate is restored by - // sigreturn(2). - // - // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented - // in inline assembly below. - // - // If the OS is broken and registers are clobbered by the child, using tgkill - // to signal the current thread increases the likelihood that this thread will - // be the one clobbered. - - uint64_t expected = 0xdeadbeeffacefeed; - SET_FP0(expected); - -#ifdef __x86_64__ - asm volatile( - "movl %[killnr], %%eax;" - "movl %[parent], %%edi;" - "movl %[tid], %%esi;" - "movl %[sig], %%edx;" - "syscall;" - : - : [ killnr ] "i"(__NR_tgkill), [ parent ] "rm"(parent), - [ tid ] "rm"(parent_tid), [ sig ] "i"(SIGUSR1) - : "rax", "rdi", "rsi", "rdx", - // Clobbered by syscall. - "rcx", "r11"); -#elif __aarch64__ - asm volatile( - "mov x8, %0\n" - "mov x0, %1\n" - "mov x1, %2\n" - "mov x2, %3\n" - "svc #0\n" ::"r"(__NR_tgkill), - "r"(parent), "r"(parent_tid), "r"(SIGUSR1)); -#endif - - uint64_t got; - GET_FP0(got); - - if (getpid() == parent) { // Parent. - int status; - ASSERT_THAT(waitpid(child, &status, 0), SyscallSucceedsWithValue(child)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - } - - // TEST_CHECK_MSG since this may run in the child. - TEST_CHECK_MSG(expected == got, "Bad xmm0 value"); - - if (getpid() != parent) { // Child. - _exit(0); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fpsig_nested.cc b/test/syscalls/linux/fpsig_nested.cc deleted file mode 100644 index 302d928d1..000000000 --- a/test/syscalls/linux/fpsig_nested.cc +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This program verifies that application floating point state is restored -// correctly after a signal handler returns. It also verifies that this works -// with nested signals. -#include <sys/time.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#ifdef __x86_64__ -#define GET_XMM(__var, __xmm) \ - asm volatile("movq %%" #__xmm ", %0" : "=r"(__var)) -#define SET_XMM(__var, __xmm) asm volatile("movq %0, %%" #__xmm : : "r"(__var)) -#define GET_FP0(__var) GET_XMM(__var, xmm0) -#define SET_FP0(__var) SET_XMM(__var, xmm0) -#elif __aarch64__ -#define __stringify_1(x...) #x -#define __stringify(x...) __stringify_1(x) -#define GET_FPREG(var, regname) \ - asm volatile("str " __stringify(regname) ", %0" : "=m"(var)) -#define SET_FPREG(var, regname) \ - asm volatile("ldr " __stringify(regname) ", %0" : "=m"(var)) -#define GET_FP0(var) GET_FPREG(var, d0) -#define SET_FP0(var) SET_FPREG(var, d0) -#endif - -int pid; -int tid; - -volatile uint64_t entryxmm[2] = {~0UL, ~0UL}; -volatile uint64_t exitxmm[2]; - -void sigusr2(int s, siginfo_t* siginfo, void* _uc) { - uint64_t val = SIGUSR2; - - // Record the value of %xmm0 on entry and then clobber it. - GET_FP0(entryxmm[1]); - SET_FP0(val); - GET_FP0(exitxmm[1]); -} - -void sigusr1(int s, siginfo_t* siginfo, void* _uc) { - uint64_t val = SIGUSR1; - - // Record the value of %xmm0 on entry and then clobber it. - GET_FP0(entryxmm[0]); - SET_FP0(val); - - // Send a SIGUSR2 to ourself. The signal mask is configured such that - // the SIGUSR2 handler will run before this handler returns. -#ifdef __x86_64__ - asm volatile( - "movl %[killnr], %%eax;" - "movl %[pid], %%edi;" - "movl %[tid], %%esi;" - "movl %[sig], %%edx;" - "syscall;" - : - : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid), - [ sig ] "i"(SIGUSR2) - : "rax", "rdi", "rsi", "rdx", - // Clobbered by syscall. - "rcx", "r11"); -#elif __aarch64__ - asm volatile( - "mov x8, %0\n" - "mov x0, %1\n" - "mov x1, %2\n" - "mov x2, %3\n" - "svc #0\n" ::"r"(__NR_tgkill), - "r"(pid), "r"(tid), "r"(SIGUSR2)); -#endif - - // Record value of %xmm0 again to verify that the nested signal handler - // does not clobber it. - GET_FP0(exitxmm[0]); -} - -TEST(FPSigTest, NestedSignals) { - pid = getpid(); - tid = gettid(); - - struct sigaction sa = {}; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = sigusr1; - ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); - - sa.sa_sigaction = sigusr2; - ASSERT_THAT(sigaction(SIGUSR2, &sa, nullptr), SyscallSucceeds()); - - // The amd64 ABI specifies that the XMM register set is caller-saved. This - // implies that if there is any function call between SET_XMM and GET_XMM the - // compiler might save/restore xmm0 implicitly. This defeats the entire - // purpose of the test which is to verify that fpstate is restored by - // sigreturn(2). - // - // This is the reason why 'tgkill(getpid(), gettid(), SIGUSR1)' is implemented - // in inline assembly below. - // - // If the OS is broken and registers are clobbered by the signal, using tgkill - // to signal the current thread ensures that this is the clobbered thread. - - uint64_t expected = 0xdeadbeeffacefeed; - SET_FP0(expected); - -#ifdef __x86_64__ - asm volatile( - "movl %[killnr], %%eax;" - "movl %[pid], %%edi;" - "movl %[tid], %%esi;" - "movl %[sig], %%edx;" - "syscall;" - : - : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid), - [ sig ] "i"(SIGUSR1) - : "rax", "rdi", "rsi", "rdx", - // Clobbered by syscall. - "rcx", "r11"); -#elif __aarch64__ - asm volatile( - "mov x8, %0\n" - "mov x0, %1\n" - "mov x1, %2\n" - "mov x2, %3\n" - "svc #0\n" ::"r"(__NR_tgkill), - "r"(pid), "r"(tid), "r"(SIGUSR1)); -#endif - - uint64_t got; - GET_FP0(got); - - // - // The checks below verifies the following: - // - signal handlers must called with a clean fpu state. - // - sigreturn(2) must restore fpstate of the interrupted context. - // - EXPECT_EQ(expected, got); - EXPECT_EQ(entryxmm[0], 0); - EXPECT_EQ(entryxmm[1], 0); - EXPECT_EQ(exitxmm[0], SIGUSR1); - EXPECT_EQ(exitxmm[1], SIGUSR2); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/fsync.cc b/test/syscalls/linux/fsync.cc deleted file mode 100644 index e7e057f06..000000000 --- a/test/syscalls/linux/fsync.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <stdio.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(FsyncTest, TempFileSucceeds) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - const std::string data = "some data to sync"; - EXPECT_THAT(write(fd.get(), data.c_str(), data.size()), - SyscallSucceedsWithValue(data.size())); - EXPECT_THAT(fsync(fd.get()), SyscallSucceeds()); -} - -TEST(FsyncTest, TempDirSucceeds) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); - EXPECT_THAT(fsync(fd.get()), SyscallSucceeds()); -} - -TEST(FsyncTest, CannotFsyncOnUnopenedFd) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - int fd; - ASSERT_THAT(fd = open(file.path().c_str(), O_RDONLY), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - - // fd is now invalid. - EXPECT_THAT(fsync(fd), SyscallFailsWithErrno(EBADF)); -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/futex.cc b/test/syscalls/linux/futex.cc deleted file mode 100644 index 90b1f0508..000000000 --- a/test/syscalls/linux/futex.cc +++ /dev/null @@ -1,834 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <linux/futex.h> -#include <linux/types.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <sys/types.h> -#include <syscall.h> -#include <unistd.h> - -#include <algorithm> -#include <atomic> -#include <memory> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/memory_util.h" -#include "test/util/save_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/time_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Amount of time we wait for threads doing futex_wait to start running before -// doing futex_wake. -constexpr auto kWaiterStartupDelay = absl::Seconds(3); - -// Default timeout for waiters in tests where we expect a futex_wake to be -// ineffective. -constexpr auto kIneffectiveWakeTimeout = absl::Seconds(6); - -static_assert(kWaiterStartupDelay < kIneffectiveWakeTimeout, - "futex_wait will time out before futex_wake is called"); - -int futex_wait(bool priv, std::atomic<int>* uaddr, int val, - absl::Duration timeout = absl::InfiniteDuration()) { - int op = FUTEX_WAIT; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - - if (timeout == absl::InfiniteDuration()) { - return RetryEINTR(syscall)(SYS_futex, uaddr, op, val, nullptr); - } - - // FUTEX_WAIT doesn't adjust the timeout if it returns EINTR, so we have to do - // so. - while (true) { - auto const timeout_ts = absl::ToTimespec(timeout); - MonotonicTimer timer; - timer.Start(); - int const ret = syscall(SYS_futex, uaddr, op, val, &timeout_ts); - if (ret != -1 || errno != EINTR) { - return ret; - } - timeout = std::max(timeout - timer.Duration(), absl::ZeroDuration()); - } -} - -int futex_wait_bitset(bool priv, std::atomic<int>* uaddr, int val, int bitset, - absl::Time deadline = absl::InfiniteFuture()) { - int op = FUTEX_WAIT_BITSET | FUTEX_CLOCK_REALTIME; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - - auto const deadline_ts = absl::ToTimespec(deadline); - return RetryEINTR(syscall)( - SYS_futex, uaddr, op, val, - deadline == absl::InfiniteFuture() ? nullptr : &deadline_ts, nullptr, - bitset); -} - -int futex_wake(bool priv, std::atomic<int>* uaddr, int count) { - int op = FUTEX_WAKE; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - return syscall(SYS_futex, uaddr, op, count); -} - -int futex_wake_bitset(bool priv, std::atomic<int>* uaddr, int count, - int bitset) { - int op = FUTEX_WAKE_BITSET; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - return syscall(SYS_futex, uaddr, op, count, nullptr, nullptr, bitset); -} - -int futex_wake_op(bool priv, std::atomic<int>* uaddr1, std::atomic<int>* uaddr2, - int nwake1, int nwake2, uint32_t sub_op) { - int op = FUTEX_WAKE_OP; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - return syscall(SYS_futex, uaddr1, op, nwake1, nwake2, uaddr2, sub_op); -} - -int futex_lock_pi(bool priv, std::atomic<int>* uaddr) { - int op = FUTEX_LOCK_PI; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - int zero = 0; - if (uaddr->compare_exchange_strong(zero, gettid())) { - return 0; - } - return RetryEINTR(syscall)(SYS_futex, uaddr, op, nullptr, nullptr); -} - -int futex_trylock_pi(bool priv, std::atomic<int>* uaddr) { - int op = FUTEX_TRYLOCK_PI; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - int zero = 0; - if (uaddr->compare_exchange_strong(zero, gettid())) { - return 0; - } - return RetryEINTR(syscall)(SYS_futex, uaddr, op, nullptr, nullptr); -} - -int futex_unlock_pi(bool priv, std::atomic<int>* uaddr) { - int op = FUTEX_UNLOCK_PI; - if (priv) { - op |= FUTEX_PRIVATE_FLAG; - } - int tid = gettid(); - if (uaddr->compare_exchange_strong(tid, 0)) { - return 0; - } - return RetryEINTR(syscall)(SYS_futex, uaddr, op, nullptr, nullptr); -} - -// Fixture for futex tests parameterized by whether to use private or shared -// futexes. -class PrivateAndSharedFutexTest : public ::testing::TestWithParam<bool> { - protected: - bool IsPrivate() const { return GetParam(); } - int PrivateFlag() const { return IsPrivate() ? FUTEX_PRIVATE_FLAG : 0; } -}; - -// FUTEX_WAIT with 0 timeout does not block. -TEST_P(PrivateAndSharedFutexTest, Wait_ZeroTimeout) { - struct timespec timeout = {}; - - // Don't use the futex_wait helper because it adjusts timeout. - int a = 1; - EXPECT_THAT(syscall(SYS_futex, &a, FUTEX_WAIT | PrivateFlag(), a, &timeout), - SyscallFailsWithErrno(ETIMEDOUT)); -} - -TEST_P(PrivateAndSharedFutexTest, Wait_Timeout) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - - MonotonicTimer timer; - timer.Start(); - constexpr absl::Duration kTimeout = absl::Seconds(1); - EXPECT_THAT(futex_wait(IsPrivate(), &a, a, kTimeout), - SyscallFailsWithErrno(ETIMEDOUT)); - EXPECT_GE(timer.Duration(), kTimeout); -} - -TEST_P(PrivateAndSharedFutexTest, Wait_BitsetTimeout) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - - MonotonicTimer timer; - timer.Start(); - constexpr absl::Duration kTimeout = absl::Seconds(1); - EXPECT_THAT( - futex_wait_bitset(IsPrivate(), &a, a, 0xffffffff, absl::Now() + kTimeout), - SyscallFailsWithErrno(ETIMEDOUT)); - EXPECT_GE(timer.Duration(), kTimeout); -} - -TEST_P(PrivateAndSharedFutexTest, WaitBitset_NegativeTimeout) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - - MonotonicTimer timer; - timer.Start(); - EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, a, 0xffffffff, - absl::Now() - absl::Seconds(1)), - SyscallFailsWithErrno(ETIMEDOUT)); -} - -TEST_P(PrivateAndSharedFutexTest, Wait_WrongVal) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - EXPECT_THAT(futex_wait(IsPrivate(), &a, a + 1), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(PrivateAndSharedFutexTest, Wait_ZeroBitset) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, a, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(PrivateAndSharedFutexTest, Wake1_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - // Prevent save/restore from interrupting futex_wait, which will cause it to - // return EAGAIN instead of the expected result if futex_wait is restarted - // after we change the value of a below. - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), - SyscallSucceedsWithValue(0)); - }); - absl::SleepFor(kWaiterStartupDelay); - - // Change a so that if futex_wake happens before futex_wait, the latter - // returns EAGAIN instead of hanging the test. - a.fetch_add(1); - EXPECT_THAT(futex_wake(IsPrivate(), &a, 1), SyscallSucceedsWithValue(1)); -} - -TEST_P(PrivateAndSharedFutexTest, Wake0_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - // Prevent save/restore from interrupting futex_wait, which will cause it to - // return EAGAIN instead of the expected result if futex_wait is restarted - // after we change the value of a below. - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), - SyscallSucceedsWithValue(0)); - }); - absl::SleepFor(kWaiterStartupDelay); - - // Change a so that if futex_wake happens before futex_wait, the latter - // returns EAGAIN instead of hanging the test. - a.fetch_add(1); - // The Linux kernel wakes one waiter even if val is 0 or negative. - EXPECT_THAT(futex_wake(IsPrivate(), &a, 0), SyscallSucceedsWithValue(1)); -} - -TEST_P(PrivateAndSharedFutexTest, WakeAll_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - constexpr int kThreads = 5; - std::vector<std::unique_ptr<ScopedThread>> threads; - threads.reserve(kThreads); - for (int i = 0; i < kThreads; i++) { - threads.push_back(absl::make_unique<ScopedThread>([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), - SyscallSucceeds()); - })); - } - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - EXPECT_THAT(futex_wake(IsPrivate(), &a, kThreads), - SyscallSucceedsWithValue(kThreads)); -} - -TEST_P(PrivateAndSharedFutexTest, WakeSome_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - constexpr int kThreads = 5; - constexpr int kWokenThreads = 3; - static_assert(kWokenThreads < kThreads, - "can't wake more threads than are created"); - std::vector<std::unique_ptr<ScopedThread>> threads; - threads.reserve(kThreads); - std::vector<int> rets; - rets.reserve(kThreads); - std::vector<int> errs; - errs.reserve(kThreads); - for (int i = 0; i < kThreads; i++) { - rets.push_back(-1); - errs.push_back(0); - } - for (int i = 0; i < kThreads; i++) { - threads.push_back(absl::make_unique<ScopedThread>([&, i] { - rets[i] = - futex_wait(IsPrivate(), &a, kInitialValue, kIneffectiveWakeTimeout); - errs[i] = errno; - })); - } - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - EXPECT_THAT(futex_wake(IsPrivate(), &a, kWokenThreads), - SyscallSucceedsWithValue(kWokenThreads)); - - int woken = 0; - int timedout = 0; - for (int i = 0; i < kThreads; i++) { - threads[i]->Join(); - if (rets[i] == 0) { - woken++; - } else if (errs[i] == ETIMEDOUT) { - timedout++; - } else { - ADD_FAILURE() << " thread " << i << ": returned " << rets[i] << ", errno " - << errs[i]; - } - } - EXPECT_EQ(woken, kWokenThreads); - EXPECT_EQ(timedout, kThreads - kWokenThreads); -} - -TEST_P(PrivateAndSharedFutexTest, WaitBitset_Wake_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, kInitialValue, 0b01001000), - SyscallSucceeds()); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - EXPECT_THAT(futex_wake(IsPrivate(), &a, 1), SyscallSucceedsWithValue(1)); -} - -TEST_P(PrivateAndSharedFutexTest, Wait_WakeBitset_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), SyscallSucceeds()); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - EXPECT_THAT(futex_wake_bitset(IsPrivate(), &a, 1, 0b01001000), - SyscallSucceedsWithValue(1)); -} - -TEST_P(PrivateAndSharedFutexTest, WaitBitset_WakeBitsetMatch_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - constexpr int kBitset = 0b01001000; - - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, kInitialValue, kBitset), - SyscallSucceeds()); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - EXPECT_THAT(futex_wake_bitset(IsPrivate(), &a, 1, kBitset), - SyscallSucceedsWithValue(1)); -} - -TEST_P(PrivateAndSharedFutexTest, WaitBitset_WakeBitsetNoMatch_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - constexpr int kWaitBitset = 0b01000001; - constexpr int kWakeBitset = 0b00101000; - static_assert((kWaitBitset & kWakeBitset) == 0, - "futex_wake_bitset will wake waiter"); - - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait_bitset(IsPrivate(), &a, kInitialValue, kWaitBitset, - absl::Now() + kIneffectiveWakeTimeout), - SyscallFailsWithErrno(ETIMEDOUT)); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - EXPECT_THAT(futex_wake_bitset(IsPrivate(), &a, 1, kWakeBitset), - SyscallSucceedsWithValue(0)); -} - -TEST_P(PrivateAndSharedFutexTest, WakeOpCondSuccess_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - std::atomic<int> b = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - ScopedThread thread_a([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), SyscallSucceeds()); - }); - ScopedThread thread_b([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &b, kInitialValue), SyscallSucceeds()); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - b.fetch_add(1); - // This futex_wake_op should: - // - Wake 1 waiter on a unconditionally. - // - Wake 1 waiter on b if b == kInitialValue + 1, which it is. - // - Do "b += 1". - EXPECT_THAT(futex_wake_op(IsPrivate(), &a, &b, 1, 1, - FUTEX_OP(FUTEX_OP_ADD, 1, FUTEX_OP_CMP_EQ, - (kInitialValue + 1))), - SyscallSucceedsWithValue(2)); - EXPECT_EQ(b, kInitialValue + 2); -} - -TEST_P(PrivateAndSharedFutexTest, WakeOpCondFailure_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - std::atomic<int> b = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - ScopedThread thread_a([&] { - EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue), SyscallSucceeds()); - }); - ScopedThread thread_b([&] { - EXPECT_THAT( - futex_wait(IsPrivate(), &b, kInitialValue, kIneffectiveWakeTimeout), - SyscallFailsWithErrno(ETIMEDOUT)); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - b.fetch_add(1); - // This futex_wake_op should: - // - Wake 1 waiter on a unconditionally. - // - Wake 1 waiter on b if b == kInitialValue - 1, which it isn't. - // - Do "b += 1". - EXPECT_THAT(futex_wake_op(IsPrivate(), &a, &b, 1, 1, - FUTEX_OP(FUTEX_OP_ADD, 1, FUTEX_OP_CMP_EQ, - (kInitialValue - 1))), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(b, kInitialValue + 2); -} - -TEST_P(PrivateAndSharedFutexTest, NoWakeInterprocessPrivateAnon_NoRandomSave) { - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - auto const ptr = static_cast<std::atomic<int>*>(mapping.ptr()); - constexpr int kInitialValue = 1; - ptr->store(kInitialValue); - - DisableSave ds; - pid_t const child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(futex_wait(IsPrivate(), ptr, kInitialValue, - kIneffectiveWakeTimeout) == -1 && - errno == ETIMEDOUT); - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - absl::SleepFor(kWaiterStartupDelay); - - EXPECT_THAT(futex_wake(IsPrivate(), ptr, 1), SyscallSucceedsWithValue(0)); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST_P(PrivateAndSharedFutexTest, WakeAfterCOWBreak_NoRandomSave) { - // Use a futex on a non-stack mapping so we can be sure that the child process - // below isn't the one that breaks copy-on-write. - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - auto const ptr = static_cast<std::atomic<int>*>(mapping.ptr()); - constexpr int kInitialValue = 1; - ptr->store(kInitialValue); - - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT(futex_wait(IsPrivate(), ptr, kInitialValue), SyscallSucceeds()); - }); - absl::SleepFor(kWaiterStartupDelay); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // Wait to be killed by the parent. - while (true) pause(); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - auto cleanup_child = Cleanup([&] { - EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; - }); - - // In addition to preventing a late futex_wait from sleeping, this breaks - // copy-on-write on the mapped page. - ptr->fetch_add(1); - EXPECT_THAT(futex_wake(IsPrivate(), ptr, 1), SyscallSucceedsWithValue(1)); -} - -TEST_P(PrivateAndSharedFutexTest, WakeWrongKind_NoRandomSave) { - constexpr int kInitialValue = 1; - std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue); - - DisableSave ds; - ScopedThread thread([&] { - EXPECT_THAT( - futex_wait(IsPrivate(), &a, kInitialValue, kIneffectiveWakeTimeout), - SyscallFailsWithErrno(ETIMEDOUT)); - }); - absl::SleepFor(kWaiterStartupDelay); - - a.fetch_add(1); - // The value of priv passed to futex_wake is the opposite of that passed to - // the futex_waiter; we expect this not to wake the waiter. - EXPECT_THAT(futex_wake(!IsPrivate(), &a, 1), SyscallSucceedsWithValue(0)); -} - -INSTANTIATE_TEST_SUITE_P(SharedPrivate, PrivateAndSharedFutexTest, - ::testing::Bool()); - -// Passing null as the address only works for private futexes. - -TEST(PrivateFutexTest, WakeOp0Set) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - - int futex_op = FUTEX_OP(FUTEX_OP_SET, 2, 0, 0); - EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(a, 2); -} - -TEST(PrivateFutexTest, WakeOp0Add) { - std::atomic<int> a = ATOMIC_VAR_INIT(1); - int futex_op = FUTEX_OP(FUTEX_OP_ADD, 1, 0, 0); - EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(a, 2); -} - -TEST(PrivateFutexTest, WakeOp0Or) { - std::atomic<int> a = ATOMIC_VAR_INIT(0b01); - int futex_op = FUTEX_OP(FUTEX_OP_OR, 0b10, 0, 0); - EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(a, 0b11); -} - -TEST(PrivateFutexTest, WakeOp0Andn) { - std::atomic<int> a = ATOMIC_VAR_INIT(0b11); - int futex_op = FUTEX_OP(FUTEX_OP_ANDN, 0b10, 0, 0); - EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(a, 0b01); -} - -TEST(PrivateFutexTest, WakeOp0Xor) { - std::atomic<int> a = ATOMIC_VAR_INIT(0b1010); - int futex_op = FUTEX_OP(FUTEX_OP_XOR, 0b1100, 0, 0); - EXPECT_THAT(futex_wake_op(true, nullptr, &a, 0, 0, futex_op), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(a, 0b0110); -} - -TEST(SharedFutexTest, WakeInterprocessSharedAnon_NoRandomSave) { - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED)); - auto const ptr = static_cast<std::atomic<int>*>(mapping.ptr()); - constexpr int kInitialValue = 1; - ptr->store(kInitialValue); - - DisableSave ds; - pid_t const child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(futex_wait(false, ptr, kInitialValue) == 0); - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - auto kill_child = Cleanup( - [&] { EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); }); - absl::SleepFor(kWaiterStartupDelay); - - ptr->fetch_add(1); - // This is an ASSERT so that if it fails, we immediately abort the test (and - // kill the subprocess). - ASSERT_THAT(futex_wake(false, ptr, 1), SyscallSucceedsWithValue(1)); - - kill_child.Release(); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST(SharedFutexTest, WakeInterprocessFile_NoRandomSave) { - auto const file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - ASSERT_THAT(truncate(file.path().c_str(), kPageSize), SyscallSucceeds()); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); - auto const ptr = static_cast<std::atomic<int>*>(mapping.ptr()); - constexpr int kInitialValue = 1; - ptr->store(kInitialValue); - - DisableSave ds; - pid_t const child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(futex_wait(false, ptr, kInitialValue) == 0); - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - auto kill_child = Cleanup( - [&] { EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); }); - absl::SleepFor(kWaiterStartupDelay); - - ptr->fetch_add(1); - // This is an ASSERT so that if it fails, we immediately abort the test (and - // kill the subprocess). - ASSERT_THAT(futex_wake(false, ptr, 1), SyscallSucceedsWithValue(1)); - - kill_child.Release(); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST_P(PrivateAndSharedFutexTest, PIBasic) { - std::atomic<int> a = ATOMIC_VAR_INIT(0); - - ASSERT_THAT(futex_lock_pi(IsPrivate(), &a), SyscallSucceeds()); - EXPECT_EQ(a.load(), gettid()); - EXPECT_THAT(futex_lock_pi(IsPrivate(), &a), SyscallFailsWithErrno(EDEADLK)); - - ASSERT_THAT(futex_unlock_pi(IsPrivate(), &a), SyscallSucceeds()); - EXPECT_EQ(a.load(), 0); - EXPECT_THAT(futex_unlock_pi(IsPrivate(), &a), SyscallFailsWithErrno(EPERM)); -} - -TEST_P(PrivateAndSharedFutexTest, PIConcurrency_NoRandomSave) { - DisableSave ds; // Too many syscalls. - - std::atomic<int> a = ATOMIC_VAR_INIT(0); - const bool is_priv = IsPrivate(); - - std::unique_ptr<ScopedThread> threads[100]; - for (size_t i = 0; i < ABSL_ARRAYSIZE(threads); ++i) { - threads[i] = absl::make_unique<ScopedThread>([is_priv, &a] { - for (size_t j = 0; j < 10; ++j) { - ASSERT_THAT(futex_lock_pi(is_priv, &a), SyscallSucceeds()); - EXPECT_EQ(a.load() & FUTEX_TID_MASK, gettid()); - SleepSafe(absl::Milliseconds(5)); - ASSERT_THAT(futex_unlock_pi(is_priv, &a), SyscallSucceeds()); - } - }); - } -} - -TEST_P(PrivateAndSharedFutexTest, PIWaiters) { - std::atomic<int> a = ATOMIC_VAR_INIT(0); - const bool is_priv = IsPrivate(); - - ASSERT_THAT(futex_lock_pi(is_priv, &a), SyscallSucceeds()); - EXPECT_EQ(a.load(), gettid()); - - ScopedThread th([is_priv, &a] { - ASSERT_THAT(futex_lock_pi(is_priv, &a), SyscallSucceeds()); - ASSERT_THAT(futex_unlock_pi(is_priv, &a), SyscallSucceeds()); - }); - - // Wait until the thread blocks on the futex, setting the waiters bit. - auto start = absl::Now(); - while (a.load() != (FUTEX_WAITERS | gettid())) { - ASSERT_LT(absl::Now() - start, absl::Seconds(5)); - absl::SleepFor(absl::Milliseconds(100)); - } - ASSERT_THAT(futex_unlock_pi(is_priv, &a), SyscallSucceeds()); -} - -TEST_P(PrivateAndSharedFutexTest, PITryLock) { - std::atomic<int> a = ATOMIC_VAR_INIT(0); - const bool is_priv = IsPrivate(); - - ASSERT_THAT(futex_trylock_pi(IsPrivate(), &a), SyscallSucceeds()); - EXPECT_EQ(a.load(), gettid()); - - EXPECT_THAT(futex_trylock_pi(is_priv, &a), SyscallFailsWithErrno(EDEADLK)); - ScopedThread th([is_priv, &a] { - EXPECT_THAT(futex_trylock_pi(is_priv, &a), SyscallFailsWithErrno(EAGAIN)); - }); - th.Join(); - - ASSERT_THAT(futex_unlock_pi(IsPrivate(), &a), SyscallSucceeds()); -} - -TEST_P(PrivateAndSharedFutexTest, PITryLockConcurrency_NoRandomSave) { - DisableSave ds; // Too many syscalls. - - std::atomic<int> a = ATOMIC_VAR_INIT(0); - const bool is_priv = IsPrivate(); - - std::unique_ptr<ScopedThread> threads[10]; - for (size_t i = 0; i < ABSL_ARRAYSIZE(threads); ++i) { - threads[i] = absl::make_unique<ScopedThread>([is_priv, &a] { - for (size_t j = 0; j < 10;) { - if (futex_trylock_pi(is_priv, &a) == 0) { - ++j; - EXPECT_EQ(a.load() & FUTEX_TID_MASK, gettid()); - SleepSafe(absl::Milliseconds(5)); - ASSERT_THAT(futex_unlock_pi(is_priv, &a), SyscallSucceeds()); - } - } - }); - } -} - -int get_robust_list(int pid, struct robust_list_head** head_ptr, - size_t* len_ptr) { - return syscall(__NR_get_robust_list, pid, head_ptr, len_ptr); -} - -int set_robust_list(struct robust_list_head* head, size_t len) { - return syscall(__NR_set_robust_list, head, len); -} - -TEST(RobustFutexTest, BasicSetGet) { - struct robust_list_head hd = {}; - struct robust_list_head* hd_ptr = &hd; - - // Set! - EXPECT_THAT(set_robust_list(hd_ptr, sizeof(hd)), SyscallSucceedsWithValue(0)); - - // Get! - struct robust_list_head* new_hd_ptr = hd_ptr; - size_t len; - EXPECT_THAT(get_robust_list(0, &new_hd_ptr, &len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(new_hd_ptr, hd_ptr); - EXPECT_EQ(len, sizeof(hd)); -} - -TEST(RobustFutexTest, GetFromOtherTid) { - // Get the current tid and list head. - pid_t tid = gettid(); - struct robust_list_head* hd_ptr = {}; - size_t len; - EXPECT_THAT(get_robust_list(0, &hd_ptr, &len), SyscallSucceedsWithValue(0)); - - // Create a new thread. - ScopedThread t([&] { - // Current tid list head should be different from parent tid. - struct robust_list_head* got_hd_ptr = {}; - EXPECT_THAT(get_robust_list(0, &got_hd_ptr, &len), - SyscallSucceedsWithValue(0)); - EXPECT_NE(hd_ptr, got_hd_ptr); - - // Get the parent list head by passing its tid. - EXPECT_THAT(get_robust_list(tid, &got_hd_ptr, &len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(hd_ptr, got_hd_ptr); - }); - - // Wait for thread. - t.Join(); -} - -TEST(RobustFutexTest, InvalidSize) { - struct robust_list_head* hd = {}; - EXPECT_THAT(set_robust_list(hd, sizeof(*hd) + 1), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(RobustFutexTest, PthreadMutexAttr) { - constexpr int kNumMutexes = 3; - - // Create a bunch of robust mutexes. - pthread_mutexattr_t attrs[kNumMutexes]; - pthread_mutex_t mtxs[kNumMutexes]; - for (int i = 0; i < kNumMutexes; i++) { - TEST_PCHECK(pthread_mutexattr_init(&attrs[i]) == 0); - TEST_PCHECK(pthread_mutexattr_setrobust(&attrs[i], PTHREAD_MUTEX_ROBUST) == - 0); - TEST_PCHECK(pthread_mutex_init(&mtxs[i], &attrs[i]) == 0); - } - - // Start thread to lock the mutexes and then exit. - ScopedThread t([&] { - for (int i = 0; i < kNumMutexes; i++) { - TEST_PCHECK(pthread_mutex_lock(&mtxs[i]) == 0); - } - pthread_exit(NULL); - }); - - // Wait for thread. - t.Join(); - - // Now try to take the mutexes. - for (int i = 0; i < kNumMutexes; i++) { - // Should get EOWNERDEAD. - EXPECT_EQ(pthread_mutex_lock(&mtxs[i]), EOWNERDEAD); - // Make the mutex consistent. - EXPECT_EQ(pthread_mutex_consistent(&mtxs[i]), 0); - // Unlock. - EXPECT_EQ(pthread_mutex_unlock(&mtxs[i]), 0); - } -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/getcpu.cc b/test/syscalls/linux/getcpu.cc deleted file mode 100644 index f4d94bd6a..000000000 --- a/test/syscalls/linux/getcpu.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sched.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(GetcpuTest, IsValidCpuStress) { - const int num_cpus = NumCPUs(); - absl::Time deadline = absl::Now() + absl::Seconds(10); - while (absl::Now() < deadline) { - int cpu; - ASSERT_THAT(cpu = sched_getcpu(), SyscallSucceeds()); - ASSERT_LT(cpu, num_cpus); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/getdents.cc b/test/syscalls/linux/getdents.cc deleted file mode 100644 index 2f2b14037..000000000 --- a/test/syscalls/linux/getdents.cc +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <syscall.h> -#include <unistd.h> - -#include <map> -#include <string> -#include <unordered_map> -#include <unordered_set> -#include <utility> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/container/node_hash_map.h" -#include "absl/container/node_hash_set.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::Contains; -using ::testing::IsEmpty; -using ::testing::IsSupersetOf; -using ::testing::Not; -using ::testing::NotNull; - -namespace gvisor { -namespace testing { - -namespace { - -// New Linux dirent format. -struct linux_dirent64 { - uint64_t d_ino; // Inode number - int64_t d_off; // Offset to next linux_dirent64 - unsigned short d_reclen; // NOLINT, Length of this linux_dirent64 - unsigned char d_type; // NOLINT, File type - char d_name[0]; // Filename (null-terminated) -}; - -// Old Linux dirent format. -struct linux_dirent { - unsigned long d_ino; // NOLINT - unsigned long d_off; // NOLINT - unsigned short d_reclen; // NOLINT - char d_name[0]; -}; - -// Wraps a buffer to provide a set of dirents. -// T is the underlying dirent type. -template <typename T> -class DirentBuffer { - public: - // DirentBuffer manages the buffer. - explicit DirentBuffer(size_t size) - : managed_(true), actual_size_(size), reported_size_(size) { - data_ = new char[actual_size_]; - } - - // The buffer is managed externally. - DirentBuffer(char* data, size_t actual_size, size_t reported_size) - : managed_(false), - data_(data), - actual_size_(actual_size), - reported_size_(reported_size) {} - - ~DirentBuffer() { - if (managed_) { - delete[] data_; - } - } - - T* Data() { return reinterpret_cast<T*>(data_); } - - T* Start(size_t read) { - read_ = read; - if (read_) { - return Data(); - } else { - return nullptr; - } - } - - T* Current() { return reinterpret_cast<T*>(&data_[off_]); } - - T* Next() { - size_t new_off = off_ + Current()->d_reclen; - if (new_off >= read_ || new_off >= actual_size_) { - return nullptr; - } - - off_ = new_off; - return Current(); - } - - size_t Size() { return reported_size_; } - - void Reset() { - off_ = 0; - read_ = 0; - memset(data_, 0, actual_size_); - } - - private: - bool managed_; - char* data_; - size_t actual_size_; - size_t reported_size_; - - size_t off_ = 0; - - size_t read_ = 0; -}; - -// Test for getdents/getdents64. -// T is the Linux dirent type. -template <typename T> -class GetdentsTest : public ::testing::Test { - public: - using LinuxDirentType = T; - using DirentBufferType = DirentBuffer<T>; - - protected: - void SetUp() override { - dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - fd_ = ASSERT_NO_ERRNO_AND_VALUE(Open(dir_.path(), O_RDONLY | O_DIRECTORY)); - } - - // Must be overridden with explicit specialization. See below. - int SyscallNum(); - - int Getdents(LinuxDirentType* dirp, unsigned int count) { - return RetryEINTR(syscall)(SyscallNum(), fd_.get(), dirp, count); - } - - // Fill directory with num files, named by number starting at 0. - void FillDirectory(size_t num) { - for (size_t i = 0; i < num; i++) { - auto name = JoinPath(dir_.path(), absl::StrCat(i)); - TEST_CHECK(CreateWithContents(name, "").ok()); - } - } - - // Fill directory with a given list of filenames. - void FillDirectoryWithFiles(const std::vector<std::string>& filenames) { - for (const auto& filename : filenames) { - auto name = JoinPath(dir_.path(), filename); - TEST_CHECK(CreateWithContents(name, "").ok()); - } - } - - // Seek to the start of the directory. - PosixError SeekStart() { - constexpr off_t kStartOfFile = 0; - off_t offset = lseek(fd_.get(), kStartOfFile, SEEK_SET); - if (offset < 0) { - return PosixError(errno, absl::StrCat("error seeking to ", kStartOfFile)); - } - if (offset != kStartOfFile) { - return PosixError(EINVAL, absl::StrCat("tried to seek to ", kStartOfFile, - " but got ", offset)); - } - return NoError(); - } - - // Call getdents multiple times, reading all dirents and calling f on each. - // f has the type signature PosixError f(T*). - // If f returns a non-OK error, so does ReadDirents. - template <typename F> - PosixError ReadDirents(DirentBufferType* dirents, F const& f) { - int n; - do { - dirents->Reset(); - - n = Getdents(dirents->Data(), dirents->Size()); - MaybeSave(); - if (n < 0) { - return PosixError(errno, "getdents"); - } - - for (auto d = dirents->Start(n); d; d = dirents->Next()) { - RETURN_IF_ERRNO(f(d)); - } - } while (n > 0); - - return NoError(); - } - - // Call Getdents successively and count all entries. - int ReadAndCountAllEntries(DirentBufferType* dirents) { - int found = 0; - - EXPECT_NO_ERRNO(ReadDirents(dirents, [&](LinuxDirentType* d) { - found++; - return NoError(); - })); - - return found; - } - - private: - TempPath dir_; - FileDescriptor fd_; -}; - -// Multiple template parameters are not allowed, so we must use explicit -// template specialization to set the syscall number. - -// SYS_getdents isn't defined on arm64. -#ifdef __x86_64__ -template <> -int GetdentsTest<struct linux_dirent>::SyscallNum() { - return SYS_getdents; -} -#endif - -template <> -int GetdentsTest<struct linux_dirent64>::SyscallNum() { - return SYS_getdents64; -} - -#ifdef __x86_64__ -// Test both legacy getdents and getdents64 on x86_64. -typedef ::testing::Types<struct linux_dirent, struct linux_dirent64> - GetdentsTypes; -#elif __aarch64__ -// Test only getdents64 on arm64. -typedef ::testing::Types<struct linux_dirent64> GetdentsTypes; -#endif -TYPED_TEST_SUITE(GetdentsTest, GetdentsTypes); - -// N.B. TYPED_TESTs require explicitly using this-> to access members of -// GetdentsTest, since we are inside of a derived class template. - -TYPED_TEST(GetdentsTest, VerifyEntries) { - typename TestFixture::DirentBufferType dirents(1024); - - this->FillDirectory(2); - - // Map of all the entries we expect to find. - std::map<std::string, bool> found; - found["."] = false; - found[".."] = false; - found["0"] = false; - found["1"] = false; - - EXPECT_NO_ERRNO(this->ReadDirents( - &dirents, [&](typename TestFixture::LinuxDirentType* d) { - auto kv = found.find(d->d_name); - EXPECT_NE(kv, found.end()) << "Unexpected file: " << d->d_name; - if (kv != found.end()) { - EXPECT_FALSE(kv->second); - } - found[d->d_name] = true; - return NoError(); - })); - - for (auto& kv : found) { - EXPECT_TRUE(kv.second) << "File not found: " << kv.first; - } -} - -TYPED_TEST(GetdentsTest, VerifyPadding) { - typename TestFixture::DirentBufferType dirents(1024); - - // Create files with names of length 1 through 16. - std::vector<std::string> files; - std::string filename; - for (int i = 0; i < 16; ++i) { - absl::StrAppend(&filename, "a"); - files.push_back(filename); - } - this->FillDirectoryWithFiles(files); - - // We expect to find all the files, plus '.' and '..'. - const int expect_found = 2 + files.size(); - int found = 0; - - EXPECT_NO_ERRNO(this->ReadDirents( - &dirents, [&](typename TestFixture::LinuxDirentType* d) { - EXPECT_EQ(d->d_reclen % 8, 0) - << "Dirent " << d->d_name - << " had reclen that was not byte aligned: " << d->d_name; - found++; - return NoError(); - })); - - // Make sure we found all the files. - EXPECT_EQ(found, expect_found); -} - -// For a small directory, the provided buffer should be large enough -// for all entries. -TYPED_TEST(GetdentsTest, SmallDir) { - // . and .. should be in an otherwise empty directory. - int expect = 2; - - // Add some actual files. - this->FillDirectory(2); - expect += 2; - - typename TestFixture::DirentBufferType dirents(256); - - EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); -} - -// A directory with lots of files requires calling getdents multiple times. -TYPED_TEST(GetdentsTest, LargeDir) { - // . and .. should be in an otherwise empty directory. - int expect = 2; - - // Add some actual files. - this->FillDirectory(100); - expect += 100; - - typename TestFixture::DirentBufferType dirents(256); - - EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); -} - -// If we lie about the size of the buffer, we should still be able to read the -// entries with the available space. -TYPED_TEST(GetdentsTest, PartialBuffer) { - // . and .. should be in an otherwise empty directory. - int expect = 2; - - // Add some actual files. - this->FillDirectory(100); - expect += 100; - - void* addr = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - ASSERT_NE(addr, MAP_FAILED); - - char* buf = reinterpret_cast<char*>(addr); - - // Guard page - EXPECT_THAT( - mprotect(reinterpret_cast<void*>(buf + kPageSize), kPageSize, PROT_NONE), - SyscallSucceeds()); - - // Limit space in buf to 256 bytes. - buf += kPageSize - 256; - - // Lie about the buffer. Even though we claim the buffer is 1 page, - // we should still get all of the dirents in the first 256 bytes. - typename TestFixture::DirentBufferType dirents(buf, 256, kPageSize); - - EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); - - EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds()); -} - -// Open many file descriptors, then scan through /proc/self/fd to find and close -// them all. (The latter is commonly used to handle races between fork/execve -// and the creation of unwanted non-O_CLOEXEC file descriptors.) This tests that -// getdents iterates correctly despite mutation of /proc/self/fd. -TYPED_TEST(GetdentsTest, ProcSelfFd) { - constexpr size_t kNfds = 10; - absl::node_hash_map<int, FileDescriptor> fds; - fds.reserve(kNfds); - for (size_t i = 0; i < kNfds; i++) { - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - fds.emplace(fd.get(), std::move(fd)); - } - - const FileDescriptor proc_self_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/fd", O_RDONLY | O_DIRECTORY)); - - // Make the buffer very small since we want to iterate. - typename TestFixture::DirentBufferType dirents( - 2 * sizeof(typename TestFixture::LinuxDirentType)); - absl::node_hash_set<int> prev_fds; - while (true) { - dirents.Reset(); - int rv; - ASSERT_THAT(rv = RetryEINTR(syscall)(this->SyscallNum(), proc_self_fd.get(), - dirents.Data(), dirents.Size()), - SyscallSucceeds()); - if (rv == 0) { - break; - } - for (auto* d = dirents.Start(rv); d; d = dirents.Next()) { - int dfd; - if (!absl::SimpleAtoi(d->d_name, &dfd)) continue; - EXPECT_TRUE(prev_fds.insert(dfd).second) - << "Repeated observation of /proc/self/fd/" << dfd; - fds.erase(dfd); - } - } - - // Check that we closed every fd. - EXPECT_THAT(fds, ::testing::IsEmpty()); -} - -// Test that getdents returns ENOTDIR when called on a file. -TYPED_TEST(GetdentsTest, NotDir) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - typename TestFixture::DirentBufferType dirents(256); - EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), - dirents.Size()), - SyscallFailsWithErrno(ENOTDIR)); -} - -// Test that getdents returns EBADF when called on an opath file. -TYPED_TEST(GetdentsTest, OpathFile) { - SKIP_IF(IsRunningWithVFS1()); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - typename TestFixture::DirentBufferType dirents(256); - EXPECT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), - dirents.Size()), - SyscallFailsWithErrno(EBADF)); -} - -// Test that getdents returns EBADF when called on an opath directory. -TYPED_TEST(GetdentsTest, OpathDirectory) { - SKIP_IF(IsRunningWithVFS1()); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_PATH | O_DIRECTORY)); - - typename TestFixture::DirentBufferType dirents(256); - ASSERT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), - dirents.Size()), - SyscallFailsWithErrno(EBADF)); -} - -// Test that SEEK_SET to 0 causes getdents to re-read the entries. -TYPED_TEST(GetdentsTest, SeekResetsCursor) { - // . and .. should be in an otherwise empty directory. - int expect = 2; - - // Add some files to the directory. - this->FillDirectory(10); - expect += 10; - - typename TestFixture::DirentBufferType dirents(256); - - // We should get all the expected entries. - EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); - - // Seek back to 0. - ASSERT_NO_ERRNO(this->SeekStart()); - - // We should get all the expected entries again. - EXPECT_EQ(expect, this->ReadAndCountAllEntries(&dirents)); -} - -// Test that getdents() after SEEK_END succeeds. -// This is a regression test for #128. -TYPED_TEST(GetdentsTest, Issue128ProcSeekEnd) { - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self", O_RDONLY | O_DIRECTORY)); - typename TestFixture::DirentBufferType dirents(256); - - ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(syscall)(this->SyscallNum(), fd.get(), dirents.Data(), - dirents.Size()), - SyscallSucceeds()); -} - -// Some tests using the glibc readdir interface. -TEST(ReaddirTest, OpenDir) { - DIR* dev; - ASSERT_THAT(dev = opendir("/dev"), NotNull()); - EXPECT_THAT(closedir(dev), SyscallSucceeds()); -} - -TEST(ReaddirTest, RootContainsBasicDirectories) { - EXPECT_THAT(ListDir("/", true), - IsPosixErrorOkAndHolds(IsSupersetOf( - {"bin", "dev", "etc", "lib", "proc", "sbin", "usr"}))); -} - -TEST(ReaddirTest, Bug24096713Dev) { - auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev", true)); - EXPECT_THAT(contents, Not(IsEmpty())); -} - -TEST(ReaddirTest, Bug24096713ProcTid) { - auto contents = ASSERT_NO_ERRNO_AND_VALUE( - ListDir(absl::StrCat("/proc/", syscall(SYS_gettid), "/"), true)); - EXPECT_THAT(contents, Not(IsEmpty())); -} - -TEST(ReaddirTest, Bug33429925Proc) { - auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc", true)); - EXPECT_THAT(contents, Not(IsEmpty())); -} - -TEST(ReaddirTest, Bug35110122Root) { - auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/", true)); - EXPECT_THAT(contents, Not(IsEmpty())); -} - -// Unlink should invalidate getdents cache. -TEST(ReaddirTest, GoneAfterRemoveCache) { - TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - std::string name = std::string(Basename(file.path())); - - auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), true)); - EXPECT_THAT(contents, Contains(name)); - - file.reset(); - - contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), true)); - EXPECT_THAT(contents, Not(Contains(name))); -} - -// Regression test for b/137398511. Rename should invalidate getdents cache. -TEST(ReaddirTest, GoneAfterRenameCache) { - TempPath src = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath dst = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(src.path())); - std::string name = std::string(Basename(file.path())); - - auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(src.path(), true)); - EXPECT_THAT(contents, Contains(name)); - - ASSERT_THAT(rename(file.path().c_str(), JoinPath(dst.path(), name).c_str()), - SyscallSucceeds()); - // Release file since it was renamed. dst cleanup will ultimately delete it. - file.release(); - - contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(src.path(), true)); - EXPECT_THAT(contents, Not(Contains(name))); - - contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dst.path(), true)); - EXPECT_THAT(contents, Contains(name)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/getrandom.cc b/test/syscalls/linux/getrandom.cc deleted file mode 100644 index f87cdd7a1..000000000 --- a/test/syscalls/linux/getrandom.cc +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#ifndef SYS_getrandom -#if defined(__x86_64__) -#define SYS_getrandom 318 -#elif defined(__i386__) -#define SYS_getrandom 355 -#elif defined(__aarch64__) -#define SYS_getrandom 278 -#else -#error "Unknown architecture" -#endif -#endif // SYS_getrandom - -bool SomeByteIsNonZero(char* random_bytes, int length) { - for (int i = 0; i < length; i++) { - if (random_bytes[i] != 0) { - return true; - } - } - return false; -} - -TEST(GetrandomTest, IsRandom) { - // This test calls get_random and makes sure that the array is filled in with - // something that is non-zero. Perhaps we get back \x00\x00\x00\x00\x00.... as - // a random result, but it's so unlikely that we'll just ignore this. - char random_bytes[64] = {}; - int n = syscall(SYS_getrandom, random_bytes, 64, 0); - SKIP_IF(!IsRunningOnGvisor() && n < 0 && errno == ENOSYS); - EXPECT_THAT(n, SyscallSucceeds()); - EXPECT_GT(n, 0); // Some bytes should be returned. - EXPECT_TRUE(SomeByteIsNonZero(random_bytes, n)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/getrusage.cc b/test/syscalls/linux/getrusage.cc deleted file mode 100644 index e84cbfdc3..000000000 --- a/test/syscalls/linux/getrusage.cc +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/types.h> -#include <sys/wait.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(GetrusageTest, BasicFork) { - pid_t pid = fork(); - if (pid == 0) { - struct rusage rusage_self; - TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); - struct rusage rusage_children; - TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); - // The child has consumed some memory. - TEST_CHECK(rusage_self.ru_maxrss != 0); - // The child has no children of its own. - TEST_CHECK(rusage_children.ru_maxrss == 0); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); - struct rusage rusage_self; - ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); - struct rusage rusage_children; - ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); - // The parent has consumed some memory. - EXPECT_GT(rusage_self.ru_maxrss, 0); - // The child has consumed some memory, and because it has exited we can get - // its max RSS. - EXPECT_GT(rusage_children.ru_maxrss, 0); -} - -// Verifies that a process can get the max resident set size of its grandchild, -// i.e. that maxrss propagates correctly from children to waiting parents. -TEST(GetrusageTest, Grandchild) { - constexpr int kGrandchildSizeKb = 1024; - pid_t pid = fork(); - if (pid == 0) { - pid = fork(); - if (pid == 0) { - int flags = MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE; - void* addr = - mmap(nullptr, kGrandchildSizeKb * 1024, PROT_WRITE, flags, -1, 0); - TEST_PCHECK(addr != MAP_FAILED); - } else { - int status; - TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0) == pid); - } - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceeds()); - struct rusage rusage_self; - ASSERT_THAT(getrusage(RUSAGE_SELF, &rusage_self), SyscallSucceeds()); - struct rusage rusage_children; - ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &rusage_children), SyscallSucceeds()); - // The parent has consumed some memory. - EXPECT_GT(rusage_self.ru_maxrss, 0); - // The child should consume next to no memory, but the grandchild will - // consume at least 1MB. Verify that usage bubbles up to the grandparent. - EXPECT_GT(rusage_children.ru_maxrss, kGrandchildSizeKb); -} - -// Verifies that processes ignoring SIGCHLD do not have updated child maxrss -// updated. -TEST(GetrusageTest, IgnoreSIGCHLD) { - const auto rest = [] { - struct sigaction sa; - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - auto cleanup = TEST_CHECK_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); - pid_t pid = fork(); - if (pid == 0) { - struct rusage rusage_self; - TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); - // The child has consumed some memory. - TEST_CHECK(rusage_self.ru_maxrss != 0); - _exit(0); - } - TEST_CHECK_SUCCESS(pid); - int status; - TEST_CHECK_ERRNO(RetryEINTR(waitpid)(pid, &status, 0), ECHILD); - struct rusage rusage_self; - TEST_CHECK_SUCCESS(getrusage(RUSAGE_SELF, &rusage_self)); - struct rusage rusage_children; - TEST_CHECK_SUCCESS(getrusage(RUSAGE_CHILDREN, &rusage_children)); - // The parent has consumed some memory. - TEST_CHECK(rusage_self.ru_maxrss > 0); - // The child's maxrss should not have propagated up. - TEST_CHECK(rusage_children.ru_maxrss == 0); - }; - // Execute inside a forked process so that rusage_children is clean. - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -// Verifies that zombie processes do not update their parent's maxrss. Only -// reaped processes should do this. -TEST(GetrusageTest, IgnoreZombie) { - const auto rest = [] { - pid_t pid = fork(); - if (pid == 0) { - struct rusage rusage_self; - TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); - struct rusage rusage_children; - TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); - // The child has consumed some memory. - TEST_CHECK(rusage_self.ru_maxrss != 0); - // The child has no children of its own. - TEST_CHECK(rusage_children.ru_maxrss == 0); - _exit(0); - } - TEST_CHECK_SUCCESS(pid); - // Give the child time to exit. Because we don't call wait, the child should - // remain a zombie. - absl::SleepFor(absl::Seconds(5)); - struct rusage rusage_self; - TEST_CHECK_SUCCESS(getrusage(RUSAGE_SELF, &rusage_self)); - struct rusage rusage_children; - TEST_CHECK_SUCCESS(getrusage(RUSAGE_CHILDREN, &rusage_children)); - // The parent has consumed some memory. - TEST_CHECK(rusage_self.ru_maxrss > 0); - // The child has consumed some memory, but hasn't been reaped. - TEST_CHECK(rusage_children.ru_maxrss == 0); - }; - // Execute inside a forked process so that rusage_children is clean. - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST(GetrusageTest, Wait4) { - pid_t pid = fork(); - if (pid == 0) { - struct rusage rusage_self; - TEST_PCHECK(getrusage(RUSAGE_SELF, &rusage_self) == 0); - struct rusage rusage_children; - TEST_PCHECK(getrusage(RUSAGE_CHILDREN, &rusage_children) == 0); - // The child has consumed some memory. - TEST_CHECK(rusage_self.ru_maxrss != 0); - // The child has no children of its own. - TEST_CHECK(rusage_children.ru_maxrss == 0); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - struct rusage rusage_children; - int status; - ASSERT_THAT(RetryEINTR(wait4)(pid, &status, 0, &rusage_children), - SyscallSucceeds()); - // The child has consumed some memory, and because it has exited we can get - // its max RSS. - EXPECT_GT(rusage_children.ru_maxrss, 0); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc deleted file mode 100644 index a88c89e20..000000000 --- a/test/syscalls/linux/inotify.cc +++ /dev/null @@ -1,2532 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <libgen.h> -#include <sched.h> -#include <sys/epoll.h> -#include <sys/inotify.h> -#include <sys/ioctl.h> -#include <sys/sendfile.h> -#include <sys/time.h> -#include <sys/xattr.h> - -#include <atomic> -#include <list> -#include <string> -#include <vector> - -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "absl/strings/str_join.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/epoll_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using ::absl::StreamFormat; -using ::absl::StrFormat; - -constexpr int kBufSize = 1024; - -// C++-friendly version of struct inotify_event. -struct Event { - int32_t wd; - uint32_t mask; - uint32_t cookie; - uint32_t len; - std::string name; - - Event(uint32_t mask, int32_t wd, absl::string_view name, uint32_t cookie) - : wd(wd), - mask(mask), - cookie(cookie), - len(name.size()), - name(std::string(name)) {} - Event(uint32_t mask, int32_t wd, absl::string_view name) - : Event(mask, wd, name, 0) {} - Event(uint32_t mask, int32_t wd) : Event(mask, wd, "", 0) {} - Event() : Event(0, 0, "", 0) {} -}; - -// Prints the symbolic name for a struct inotify_event's 'mask' field. -std::string FlagString(uint32_t flags) { - std::vector<std::string> names; - -#define EMIT(target) \ - if (flags & target) { \ - names.push_back(#target); \ - flags &= ~target; \ - } - - EMIT(IN_ACCESS); - EMIT(IN_ATTRIB); - EMIT(IN_CLOSE_WRITE); - EMIT(IN_CLOSE_NOWRITE); - EMIT(IN_CREATE); - EMIT(IN_DELETE); - EMIT(IN_DELETE_SELF); - EMIT(IN_MODIFY); - EMIT(IN_MOVE_SELF); - EMIT(IN_MOVED_FROM); - EMIT(IN_MOVED_TO); - EMIT(IN_OPEN); - - EMIT(IN_DONT_FOLLOW); - EMIT(IN_EXCL_UNLINK); - EMIT(IN_ONESHOT); - EMIT(IN_ONLYDIR); - - EMIT(IN_IGNORED); - EMIT(IN_ISDIR); - EMIT(IN_Q_OVERFLOW); - EMIT(IN_UNMOUNT); - -#undef EMIT - - // If we have anything left over at the end, print it as a hex value. - if (flags) { - names.push_back(absl::StrCat("0x", absl::Hex(flags))); - } - - return absl::StrJoin(names, "|"); -} - -std::string DumpEvent(const Event& event) { - return StrFormat( - "%s, wd=%d%s%s", FlagString(event.mask), event.wd, - (event.len > 0) ? StrFormat(", name=%s", event.name) : "", - (event.cookie > 0) ? StrFormat(", cookie=%ud", event.cookie) : ""); -} - -std::string DumpEvents(const std::vector<Event>& events, int indent_level) { - std::stringstream ss; - ss << StreamFormat("%d event%s:\n", events.size(), - (events.size() > 1) ? "s" : ""); - int i = 0; - for (const Event& ev : events) { - ss << StreamFormat("%sevents[%d]: %s\n", std::string(indent_level, '\t'), - i++, DumpEvent(ev)); - } - return ss.str(); -} - -// A matcher which takes an expected list of events to match against another -// list of inotify events, in order. This is similar to the ElementsAre matcher, -// but displays more informative messages on mismatch. -class EventsAreMatcher - : public ::testing::MatcherInterface<std::vector<Event>> { - public: - explicit EventsAreMatcher(std::vector<Event> references) - : references_(std::move(references)) {} - - bool MatchAndExplain( - std::vector<Event> events, - ::testing::MatchResultListener* const listener) const override { - if (references_.size() != events.size()) { - *listener << StreamFormat("\n\tCount mismatch, got %s", - DumpEvents(events, 2)); - return false; - } - - bool success = true; - for (unsigned int i = 0; i < references_.size(); ++i) { - const Event& reference = references_[i]; - const Event& target = events[i]; - - if (target.mask != reference.mask || target.wd != reference.wd || - target.name != reference.name || target.cookie != reference.cookie) { - *listener << StreamFormat("\n\tMismatch at index %d, want %s, got %s,", - i, DumpEvent(reference), DumpEvent(target)); - success = false; - } - } - - if (!success) { - *listener << StreamFormat("\n\tIn total of %s", DumpEvents(events, 2)); - } - return success; - } - - void DescribeTo(::std::ostream* const os) const override { - *os << StreamFormat("%s", DumpEvents(references_, 1)); - } - - void DescribeNegationTo(::std::ostream* const os) const override { - *os << StreamFormat("mismatch from %s", DumpEvents(references_, 1)); - } - - private: - std::vector<Event> references_; -}; - -::testing::Matcher<std::vector<Event>> Are(std::vector<Event> events) { - return MakeMatcher(new EventsAreMatcher(std::move(events))); -} - -// Similar to the EventsAre matcher, but the order of events are ignored. -class UnorderedEventsAreMatcher - : public ::testing::MatcherInterface<std::vector<Event>> { - public: - explicit UnorderedEventsAreMatcher(std::vector<Event> references) - : references_(std::move(references)) {} - - bool MatchAndExplain( - std::vector<Event> events, - ::testing::MatchResultListener* const listener) const override { - if (references_.size() != events.size()) { - *listener << StreamFormat("\n\tCount mismatch, got %s", - DumpEvents(events, 2)); - return false; - } - - std::vector<Event> unmatched(references_); - - for (const Event& candidate : events) { - for (auto it = unmatched.begin(); it != unmatched.end();) { - const Event& reference = *it; - if (candidate.mask == reference.mask && candidate.wd == reference.wd && - candidate.name == reference.name && - candidate.cookie == reference.cookie) { - it = unmatched.erase(it); - break; - } else { - ++it; - } - } - } - - // Anything left unmatched? If so, the matcher fails. - if (!unmatched.empty()) { - *listener << StreamFormat("\n\tFailed to match %s", - DumpEvents(unmatched, 2)); - *listener << StreamFormat("\n\tIn total of %s", DumpEvents(events, 2)); - return false; - } - - return true; - } - - void DescribeTo(::std::ostream* const os) const override { - *os << StreamFormat("unordered %s", DumpEvents(references_, 1)); - } - - void DescribeNegationTo(::std::ostream* const os) const override { - *os << StreamFormat("mismatch from unordered %s", - DumpEvents(references_, 1)); - } - - private: - std::vector<Event> references_; -}; - -::testing::Matcher<std::vector<Event>> AreUnordered(std::vector<Event> events) { - return MakeMatcher(new UnorderedEventsAreMatcher(std::move(events))); -} - -// Reads events from an inotify fd until either EOF, or read returns EAGAIN. -PosixErrorOr<std::vector<Event>> DrainEvents(int fd) { - std::vector<Event> events; - while (true) { - int events_size = 0; - if (ioctl(fd, FIONREAD, &events_size) < 0) { - return PosixError(errno, "ioctl(FIONREAD) failed on inotify fd"); - } - // Deliberately use a buffer that is larger than necessary, expecting to - // only read events_size bytes. - std::vector<char> buf(events_size + kBufSize, 0); - const ssize_t readlen = read(fd, buf.data(), buf.size()); - MaybeSave(); - // Read error? - if (readlen < 0) { - if (errno == EAGAIN) { - // If EAGAIN, no more events at the moment. Return what we have so far. - return events; - } - // Some other read error. Return an error. Right now if we encounter this - // after already reading some events, they get lost. However, we don't - // expect to see any error, and the calling test will fail immediately if - // we signal an error anyways, so this is acceptable. - return PosixError(errno, "read() failed on inotify fd"); - } - if (readlen < static_cast<int>(sizeof(struct inotify_event))) { - // Impossibly short read. - return PosixError( - EIO, - "read() didn't return enough data represent even a single event"); - } - if (readlen != events_size) { - return PosixError(EINVAL, absl::StrCat("read ", readlen, - " bytes, expected ", events_size)); - } - if (readlen == 0) { - // EOF. - return events; - } - - // Normal read. - const char* cursor = buf.data(); - while (cursor < (buf.data() + readlen)) { - struct inotify_event event = {}; - memcpy(&event, cursor, sizeof(struct inotify_event)); - - Event ev; - ev.wd = event.wd; - ev.mask = event.mask; - ev.cookie = event.cookie; - ev.len = event.len; - if (event.len > 0) { - TEST_CHECK(static_cast<int>(sizeof(struct inotify_event) + event.len) <= - readlen); - ev.name = std::string(cursor + - offsetof(struct inotify_event, name)); // NOLINT - // Name field should always be smaller than event.len, otherwise we have - // a buffer overflow. The two sizes aren't equal because the string - // constructor will stop at the first null byte, while event.name may be - // padded up to event.len using multiple null bytes. - TEST_CHECK(ev.name.size() <= event.len); - } - - events.push_back(ev); - cursor += sizeof(struct inotify_event) + event.len; - } - } -} - -PosixErrorOr<FileDescriptor> InotifyInit1(int flags) { - int fd = inotify_init1(flags); - if (fd < 0) { - return PosixError(errno, "inotify_init1() failed"); - } - return FileDescriptor(fd); -} - -PosixErrorOr<int> InotifyAddWatch(int fd, const std::string& path, - uint32_t mask) { - int wd = inotify_add_watch(fd, path.c_str(), mask); - if (wd < 0) { - return PosixError(errno, "inotify_add_watch() failed"); - } - return wd; -} - -TEST(Inotify, IllegalSeek) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); -} - -TEST(Inotify, IllegalPread) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); - int val; - EXPECT_THAT(pread(fd.get(), &val, sizeof(val), 0), - SyscallFailsWithErrno(ESPIPE)); -} - -TEST(Inotify, IllegalPwrite) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); - EXPECT_THAT(pwrite(fd.get(), "x", 1, 0), SyscallFailsWithErrno(ESPIPE)); -} - -TEST(Inotify, IllegalWrite) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); - int val = 0; - EXPECT_THAT(write(fd.get(), &val, sizeof(val)), SyscallFailsWithErrno(EBADF)); -} - -TEST(Inotify, InitFlags) { - EXPECT_THAT(inotify_init1(IN_NONBLOCK | IN_CLOEXEC), SyscallSucceeds()); - EXPECT_THAT(inotify_init1(12345), SyscallFailsWithErrno(EINVAL)); -} - -TEST(Inotify, NonBlockingReadReturnsEagain) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - std::vector<char> buf(kBufSize, 0); - - // The read below should return fail with EAGAIN because there is no data to - // read and we've specified IN_NONBLOCK. We're guaranteed that there is no - // data to read because we haven't registered any watches yet. - EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(Inotify, AddWatchOnInvalidFdFails) { - // Garbage fd. - EXPECT_THAT(inotify_add_watch(-1, "/tmp", IN_ALL_EVENTS), - SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(inotify_add_watch(1337, "/tmp", IN_ALL_EVENTS), - SyscallFailsWithErrno(EBADF)); - - // Non-inotify fds. - EXPECT_THAT(inotify_add_watch(0, "/tmp", IN_ALL_EVENTS), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(inotify_add_watch(1, "/tmp", IN_ALL_EVENTS), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(inotify_add_watch(2, "/tmp", IN_ALL_EVENTS), - SyscallFailsWithErrno(EINVAL)); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/tmp", O_RDONLY)); - EXPECT_THAT(inotify_add_watch(fd.get(), "/tmp", IN_ALL_EVENTS), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(Inotify, RemovingWatchGeneratesEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); - - // Read events, ensure the first event is IN_IGNORED. - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_THAT(events, Are({Event(IN_IGNORED, wd)})); -} - -TEST(Inotify, CanDeleteFileAfterRemovingWatch) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); - file1.reset(); -} - -TEST(Inotify, RemoveWatchAfterDeletingFileFails) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - file1.reset(); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd), Event(IN_DELETE_SELF, wd), - Event(IN_IGNORED, wd)})); - - EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallFailsWithErrno(EINVAL)); -} - -TEST(Inotify, DuplicateWatchRemovalFails) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); - EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallFailsWithErrno(EINVAL)); -} - -TEST(Inotify, ConcurrentFileDeletionAndWatchRemoval) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const std::string filename = NewTempAbsPathInDir(root.path()); - - auto file_create_delete = [filename]() { - const DisableSave ds; // Too expensive. - for (int i = 0; i < 100; ++i) { - FileDescriptor file_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT, S_IRUSR | S_IWUSR)); - // Close before unlinking (although S/R is disabled). Some filesystems - // cannot restore an open fd on an unlinked file. - file_fd.reset(); - EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds()); - } - }; - - const int shared_fd = fd.get(); // We need to pass it to the thread. - auto add_remove_watch = [shared_fd, filename]() { - for (int i = 0; i < 100; ++i) { - int wd = inotify_add_watch(shared_fd, filename.c_str(), IN_ALL_EVENTS); - MaybeSave(); - if (wd != -1) { - // Watch added successfully, try removal. - if (inotify_rm_watch(shared_fd, wd)) { - // If removal fails, the only acceptable reason is if the wd - // is invalid, which will be the case if we try to remove - // the watch after the file has been deleted. - EXPECT_EQ(errno, EINVAL); - } - } else { - // Add watch failed, this should only fail if the target file doesn't - // exist. - EXPECT_EQ(errno, ENOENT); - } - } - }; - - ScopedThread t1(file_create_delete); - ScopedThread t2(add_remove_watch); -} - -TEST(Inotify, DeletingChildGeneratesEvents) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - const std::string file1_path = file1.reset(); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - AreUnordered({Event(IN_ATTRIB, file1_wd), Event(IN_DELETE_SELF, file1_wd), - Event(IN_IGNORED, file1_wd), - Event(IN_DELETE, root_wd, Basename(file1_path))})); -} - -// Creating a file in "parent/child" should generate events for child, but not -// parent. -TEST(Inotify, CreatingFileGeneratesEvents) { - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath child = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent.path())); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), parent.path(), IN_ALL_EVENTS)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), child.path(), IN_ALL_EVENTS)); - - // Create a new file in the directory. - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(child.path())); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - - // The library function we use to create the new file opens it for writing to - // create it and sets permissions on it, so we expect the three extra events. - ASSERT_THAT(events, Are({Event(IN_CREATE, wd, Basename(file1.path())), - Event(IN_OPEN, wd, Basename(file1.path())), - Event(IN_CLOSE_WRITE, wd, Basename(file1.path())), - Event(IN_ATTRIB, wd, Basename(file1.path()))})); -} - -TEST(Inotify, ReadingFileGeneratesAccessEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - char buf; - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_ACCESS, wd, Basename(file1.path()))})); -} - -TEST(Inotify, WritingFileGeneratesModifyEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - const std::string data = "some content"; - EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), - SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_MODIFY, wd, Basename(file1.path()))})); -} - -TEST(Inotify, SizeZeroReadWriteGeneratesNothing) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - // Read from the empty file. - int val; - ASSERT_THAT(read(file1_fd.get(), &val, sizeof(val)), - SyscallSucceedsWithValue(0)); - - // Write zero bytes. - ASSERT_THAT(write(file1_fd.get(), "", 0), SyscallSucceedsWithValue(0)); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({})); -} - -TEST(Inotify, FailedFileCreationGeneratesNoEvents) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string dir_path = dir.path(); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch(fd.get(), dir_path, IN_ALL_EVENTS)); - - const char* p = dir_path.c_str(); - ASSERT_THAT(mkdir(p, 0777), SyscallFails()); - ASSERT_THAT(mknod(p, S_IFIFO, 0777), SyscallFails()); - ASSERT_THAT(symlink(p, p), SyscallFails()); - ASSERT_THAT(link(p, p), SyscallFails()); - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({})); -} - -TEST(Inotify, WatchSetAfterOpenReportsCloseFdEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - FileDescriptor file1_fd_writable = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); - FileDescriptor file1_fd_not_writable = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - file1_fd_writable.reset(); // Close file1_fd_writable. - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_CLOSE_WRITE, wd, Basename(file1.path()))})); - - file1_fd_not_writable.reset(); // Close file1_fd_not_writable. - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, - Are({Event(IN_CLOSE_NOWRITE, wd, Basename(file1.path()))})); -} - -TEST(Inotify, ChildrenDeletionInWatchedDirGeneratesEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - const std::string file1_path = file1.reset(); - const std::string dir1_path = dir1.release(); - EXPECT_THAT(rmdir(dir1_path.c_str()), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - - ASSERT_THAT(events, - Are({Event(IN_DELETE, wd, Basename(file1_path)), - Event(IN_DELETE | IN_ISDIR, wd, Basename(dir1_path))})); -} - -TEST(Inotify, RmdirOnWatchedTargetGeneratesEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - EXPECT_THAT(rmdir(root.path().c_str()), SyscallSucceeds()); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_DELETE_SELF, wd), Event(IN_IGNORED, wd)})); -} - -TEST(Inotify, MoveGeneratesEvents) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const TempPath dir1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); - const TempPath dir2 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int dir1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), dir1.path(), IN_ALL_EVENTS)); - const int dir2_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), dir2.path(), IN_ALL_EVENTS)); - // Test move from root -> root. - std::string newpath = NewTempAbsPathInDir(root.path()); - std::string oldpath = file1.release(); - EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); - file1.reset(newpath); - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), - Event(IN_MOVED_TO, root_wd, Basename(newpath), events[1].cookie)})); - EXPECT_NE(events[0].cookie, 0); - EXPECT_EQ(events[0].cookie, events[1].cookie); - uint32_t last_cookie = events[0].cookie; - - // Test move from root -> root/dir1. - newpath = NewTempAbsPathInDir(dir1.path()); - oldpath = file1.release(); - EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); - file1.reset(newpath); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), - Event(IN_MOVED_TO, dir1_wd, Basename(newpath), events[1].cookie)})); - // Cookies should be distinct between distinct rename events. - EXPECT_NE(events[0].cookie, last_cookie); - EXPECT_EQ(events[0].cookie, events[1].cookie); - last_cookie = events[0].cookie; - - // Test move from root/dir1 -> root/dir2. - newpath = NewTempAbsPathInDir(dir2.path()); - oldpath = file1.release(); - EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); - file1.reset(newpath); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - Are({Event(IN_MOVED_FROM, dir1_wd, Basename(oldpath), events[0].cookie), - Event(IN_MOVED_TO, dir2_wd, Basename(newpath), events[1].cookie)})); - EXPECT_NE(events[0].cookie, last_cookie); - EXPECT_EQ(events[0].cookie, events[1].cookie); - last_cookie = events[0].cookie; -} - -TEST(Inotify, MoveWatchedTargetGeneratesEvents) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - const std::string newpath = NewTempAbsPathInDir(root.path()); - const std::string oldpath = file1.release(); - EXPECT_THAT(rename(oldpath.c_str(), newpath.c_str()), SyscallSucceeds()); - file1.reset(newpath); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - Are({Event(IN_MOVED_FROM, root_wd, Basename(oldpath), events[0].cookie), - Event(IN_MOVED_TO, root_wd, Basename(newpath), events[1].cookie), - // Self move events do not have a cookie. - Event(IN_MOVE_SELF, file1_wd)})); - EXPECT_NE(events[0].cookie, 0); - EXPECT_EQ(events[0].cookie, events[1].cookie); -} - -// Tests that close events are only emitted when a file description drops its -// last reference. -TEST(Inotify, DupFD) { - SKIP_IF(IsRunningWithVFS1()); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); - - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_OPEN, wd), - })); - - fd.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({})); - - fd2.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_CLOSE_NOWRITE, wd), - })); -} - -TEST(Inotify, CoalesceEvents) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - - FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - // Read the file a few times. This will would generate multiple IN_ACCESS - // events but they should get coalesced to a single event. - char buf; - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - - // Use the close event verify that we haven't simply left the additional - // IN_ACCESS events unread. - file1_fd.reset(); // Close file1_fd. - - const std::string file1_name = std::string(Basename(file1.path())); - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_ACCESS, wd, file1_name), - Event(IN_CLOSE_NOWRITE, wd, file1_name)})); - - // Now let's try interleaving other events into a stream of repeated events. - file1_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); - - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); - EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); - EXPECT_THAT(write(file1_fd.get(), "x", 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - - file1_fd.reset(); // Close the file. - - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - Are({Event(IN_OPEN, wd, file1_name), Event(IN_ACCESS, wd, file1_name), - Event(IN_MODIFY, wd, file1_name), Event(IN_ACCESS, wd, file1_name), - Event(IN_CLOSE_WRITE, wd, file1_name)})); - - // Ensure events aren't coalesced if they are from different files. - const TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - // Discard events resulting from creation of file2. - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - - file1_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - FileDescriptor file2_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file2.path(), O_RDONLY)); - - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file2_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - - // Close both files. - file1_fd.reset(); - file2_fd.reset(); - - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - const std::string file2_name = std::string(Basename(file2.path())); - ASSERT_THAT( - events, - Are({Event(IN_OPEN, wd, file1_name), Event(IN_OPEN, wd, file2_name), - Event(IN_ACCESS, wd, file1_name), Event(IN_ACCESS, wd, file2_name), - Event(IN_ACCESS, wd, file1_name), - Event(IN_CLOSE_NOWRITE, wd, file1_name), - Event(IN_CLOSE_NOWRITE, wd, file2_name)})); -} - -TEST(Inotify, ClosingInotifyFdWithoutRemovingWatchesWorks) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - // Note: The check on close will happen in FileDescriptor::~FileDescriptor(). -} - -TEST(Inotify, NestedWatches) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - // Read from file1. This should generate an event for both watches. - char buf; - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_ACCESS, root_wd, Basename(file1.path())), - Event(IN_ACCESS, file1_wd)})); -} - -TEST(Inotify, ConcurrentThreadsGeneratingEvents) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - std::vector<TempPath> files; - files.reserve(10); - for (int i = 0; i < 10; i++) { - files.emplace_back(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode))); - } - - auto test_thread = [&files]() { - uint32_t seed = time(nullptr); - for (int i = 0; i < 20; i++) { - const TempPath& file = files[rand_r(&seed) % files.size()]; - const FileDescriptor file_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - TEST_PCHECK(write(file_fd.get(), "x", 1) == 1); - } - }; - - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - std::list<ScopedThread> threads; - for (int i = 0; i < 3; i++) { - threads.emplace_back(test_thread); - } - for (auto& t : threads) { - t.Join(); - } - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - // 3 threads doing 20 iterations, 3 events per iteration (open, write, - // close). However, some events may be coalesced, and we can't reliably - // predict how they'll be coalesced since the test threads aren't - // synchronized. We can only check that we aren't getting unexpected events. - for (const Event& ev : events) { - EXPECT_NE(ev.mask & (IN_OPEN | IN_MODIFY | IN_CLOSE_WRITE), 0); - } -} - -TEST(Inotify, ReadWithTooSmallBufferFails) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - // Open the file to queue an event. This event will not have a filename, so - // reading from the inotify fd should return sizeof(struct inotify_event) - // bytes of data. - FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - std::vector<char> buf(kBufSize, 0); - ssize_t readlen; - - // Try a buffer too small to hold any potential event. This is rejected - // outright without the event being dequeued. - EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event) - 1), - SyscallFailsWithErrno(EINVAL)); - // Try a buffer just large enough. This should succeeed. - EXPECT_THAT( - readlen = read(fd.get(), buf.data(), sizeof(struct inotify_event)), - SyscallSucceeds()); - EXPECT_EQ(readlen, sizeof(struct inotify_event)); - // Event queue is now empty, the next read should return EAGAIN. - EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), - SyscallFailsWithErrno(EAGAIN)); - - // Now put a watch on the directory, so that generated events contain a name. - EXPECT_THAT(inotify_rm_watch(fd.get(), wd), SyscallSucceeds()); - - // Drain the event generated from the watch removal. - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - file1_fd.reset(); // Close file to generate an event. - - // Try a buffer too small to hold any event and one too small to hold an event - // with a name. These should both fail without consuming the event. - EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event) - 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), - SyscallFailsWithErrno(EINVAL)); - // Now try with a large enough buffer. This should return the one event. - EXPECT_THAT(readlen = read(fd.get(), buf.data(), buf.size()), - SyscallSucceeds()); - EXPECT_GE(readlen, - sizeof(struct inotify_event) + Basename(file1.path()).size()); - // With the single event read, the queue should once again be empty. - EXPECT_THAT(read(fd.get(), buf.data(), sizeof(struct inotify_event)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(Inotify, BlockingReadOnInotifyFd) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(0)); - const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - // Spawn a thread performing a blocking read for new events on the inotify fd. - std::vector<char> buf(kBufSize, 0); - const int shared_fd = fd.get(); // The thread needs it. - ScopedThread t([shared_fd, &buf]() { - ssize_t readlen; - EXPECT_THAT(readlen = read(shared_fd, buf.data(), buf.size()), - SyscallSucceeds()); - }); - - // Perform a read on the watched file, which should generate an IN_ACCESS - // event, unblocking the event_reader thread. - char c; - EXPECT_THAT(read(file1_fd.get(), &c, 1), SyscallSucceeds()); - - // Wait for the thread to read the event and exit. - t.Join(); - - // Make sure the event we got back is sane. - uint32_t event_mask; - memcpy(&event_mask, buf.data() + offsetof(struct inotify_event, mask), - sizeof(event_mask)); - EXPECT_EQ(event_mask, IN_ACCESS); -} - -TEST(Inotify, WatchOnRelativePath) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - - // Change working directory to root. - const FileDescriptor cwd = ASSERT_NO_ERRNO_AND_VALUE(Open(".", O_PATH)); - EXPECT_THAT(chdir(root.path().c_str()), SyscallSucceeds()); - - // Add a watch on file1 with a relative path. - const int wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - fd.get(), std::string(Basename(file1.path())), IN_ALL_EVENTS)); - - // Perform a read on file1, this should generate an IN_ACCESS event. - char c; - EXPECT_THAT(read(file1_fd.get(), &c, 1), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_THAT(events, Are({Event(IN_ACCESS, wd)})); - - // Explicitly reset the working directory so that we don't continue to - // reference "root". Once the test ends, "root" will get unlinked. If we - // continue to hold a reference, random save/restore tests can fail if a save - // is triggered after "root" is unlinked; we can't save deleted fs objects - // with active references. - EXPECT_THAT(fchdir(cwd.get()), SyscallSucceeds()); -} - -TEST(Inotify, ZeroLengthReadWriteDoesNotGenerateEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const char kContent[] = "some content"; - TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), kContent, TempPath::kDefaultFileMode)); - const int kContentSize = sizeof(kContent) - 1; - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - std::vector<char> buf(kContentSize, 0); - // Read all available data. - ssize_t readlen; - EXPECT_THAT(readlen = read(file1_fd.get(), buf.data(), kContentSize), - SyscallSucceeds()); - EXPECT_EQ(readlen, kContentSize); - // Drain all events and make sure we got the IN_ACCESS for the read. - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_THAT(events, Are({Event(IN_ACCESS, wd, Basename(file1.path()))})); - - // Now try read again. This should be a 0-length read, since we're at EOF. - char c; - EXPECT_THAT(readlen = read(file1_fd.get(), &c, 1), SyscallSucceeds()); - EXPECT_EQ(readlen, 0); - // We should have no new events. - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_TRUE(events.empty()); - - // Try issuing a zero-length read. - EXPECT_THAT(readlen = read(file1_fd.get(), &c, 0), SyscallSucceeds()); - EXPECT_EQ(readlen, 0); - // We should have no new events. - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_TRUE(events.empty()); - - // Try issuing a zero-length write. - ssize_t writelen; - EXPECT_THAT(writelen = write(file1_fd.get(), &c, 0), SyscallSucceeds()); - EXPECT_EQ(writelen, 0); - // We should have no new events. - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_TRUE(events.empty()); -} - -TEST(Inotify, ChmodGeneratesAttribEvent_NoRandomSave) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - FileDescriptor root_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(root.path(), O_RDONLY)); - FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - auto verify_chmod_events = [&]() { - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_ATTRIB, root_wd, Basename(file1.path())), - Event(IN_ATTRIB, file1_wd)})); - }; - - // Don't do cooperative S/R tests for any of the {f}chmod* syscalls below, the - // test will always fail because nodes cannot be saved when they have stricter - // permissions than the original host node. - const DisableSave ds; - - // Chmod. - ASSERT_THAT(chmod(file1.path().c_str(), S_IWGRP), SyscallSucceeds()); - verify_chmod_events(); - - // Fchmod. - ASSERT_THAT(fchmod(file1_fd.get(), S_IRGRP | S_IWGRP), SyscallSucceeds()); - verify_chmod_events(); - - // Fchmodat. - const std::string file1_basename = std::string(Basename(file1.path())); - ASSERT_THAT(fchmodat(root_fd.get(), file1_basename.c_str(), S_IWGRP, 0), - SyscallSucceeds()); - verify_chmod_events(); - - // Make sure the chmod'ed file descriptors are destroyed before DisableSave - // is destructed. - root_fd.reset(); - file1_fd.reset(); -} - -TEST(Inotify, TruncateGeneratesModifyEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - auto verify_truncate_events = [&]() { - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_MODIFY, root_wd, Basename(file1.path())), - Event(IN_MODIFY, file1_wd)})); - }; - - // Truncate. - EXPECT_THAT(truncate(file1.path().c_str(), 4096), SyscallSucceeds()); - verify_truncate_events(); - - // Ftruncate. - EXPECT_THAT(ftruncate(file1_fd.get(), 8192), SyscallSucceeds()); - verify_truncate_events(); - - // No events if truncate fails. - EXPECT_THAT(ftruncate(file1_fd.get(), -1), SyscallFailsWithErrno(EINVAL)); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({})); -} - -TEST(Inotify, GetdentsGeneratesAccessEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - // This internally calls getdents(2). We also expect to see an open/close - // event for the dirfd. - ASSERT_NO_ERRNO_AND_VALUE(ListDir(root.path(), false)); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - - // Linux only seems to generate access events on getdents() on some - // calls. Allow the test to pass even if it isn't generated. gVisor will - // always generate the IN_ACCESS event so the test will at least ensure gVisor - // behaves reasonably. - int i = 0; - EXPECT_EQ(events[i].mask, IN_OPEN | IN_ISDIR); - ++i; - if (IsRunningOnGvisor()) { - EXPECT_EQ(events[i].mask, IN_ACCESS | IN_ISDIR); - ++i; - } else { - if (events[i].mask == (IN_ACCESS | IN_ISDIR)) { - // Skip over the IN_ACCESS event on Linux, it only shows up some of the - // time so we can't assert its existence. - ++i; - } - } - EXPECT_EQ(events[i].mask, IN_CLOSE_NOWRITE | IN_ISDIR); -} - -TEST(Inotify, MknodGeneratesCreateEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - const TempPath file1(root.path() + "/file1"); - ASSERT_THAT(mknod(file1.path().c_str(), S_IFREG, 0), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_CREATE, wd, Basename(file1.path()))})); -} - -TEST(Inotify, SymlinkGeneratesCreateEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const TempPath link1(NewTempAbsPathInDir(root.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - ASSERT_THAT(symlink(file1.path().c_str(), link1.path().c_str()), - SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - - ASSERT_THAT(events, Are({Event(IN_CREATE, root_wd, Basename(link1.path()))})); -} - -TEST(Inotify, LinkGeneratesAttribAndCreateEvents) { - // Inotify does not work properly with hard links in gofer and overlay fs. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); - - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const TempPath link1(root.path() + "/link1"); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - ASSERT_THAT(link(file1.path().c_str(), link1.path().c_str()), - SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_ATTRIB, file1_wd), - Event(IN_CREATE, root_wd, Basename(link1.path()))})); -} - -TEST(Inotify, UtimesGeneratesAttribEvent) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDWR)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - - const struct timeval times[2] = {{1, 0}, {2, 0}}; - EXPECT_THAT(futimes(file1_fd.get(), times), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_ATTRIB, wd, Basename(file1.path()))})); -} - -TEST(Inotify, HardlinksReuseSameWatch) { - // Inotify does not work properly with hard links in gofer and overlay fs. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); - - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - TempPath file2(root.path() + "/file2"); - ASSERT_THAT(link(file.path().c_str(), file2.path().c_str()), - SyscallSucceeds()); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int root_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file.path(), IN_ALL_EVENTS)); - const int file2_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file2.path(), IN_ALL_EVENTS)); - - // The watch descriptors for watches on different links to the same file - // should be identical. - EXPECT_NE(root_wd, file_wd); - EXPECT_EQ(file_wd, file2_wd); - - FileDescriptor file_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, - AreUnordered({Event(IN_OPEN, root_wd, Basename(file.path())), - Event(IN_OPEN, file_wd)})); - - // For the next step, we want to ensure all fds to the file are closed. Do - // that now and drain the resulting events. - file_fd.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - AreUnordered({Event(IN_CLOSE_WRITE, root_wd, Basename(file.path())), - Event(IN_CLOSE_WRITE, file_wd)})); - - // Try removing the link and let's see what events show up. Note that after - // this, we still have a link to the file so the watch shouldn't be - // automatically removed. - const std::string file2_path = file2.reset(); - - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, - AreUnordered({Event(IN_ATTRIB, file2_wd), - Event(IN_DELETE, root_wd, Basename(file2_path))})); - - // Now remove the other link. Since this is the last link to the file, the - // watch should be automatically removed. - const std::string file_path = file.reset(); - - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - AreUnordered({Event(IN_ATTRIB, file_wd), Event(IN_DELETE_SELF, file_wd), - Event(IN_IGNORED, file_wd), - Event(IN_DELETE, root_wd, Basename(file_path))})); -} - -// Calling mkdir within "parent/child" should generate an event for child, but -// not parent. -TEST(Inotify, MkdirGeneratesCreateEventWithDirFlag) { - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath child = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), parent.path(), IN_ALL_EVENTS)); - const int child_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), child.path(), IN_ALL_EVENTS)); - - const TempPath dir1(NewTempAbsPathInDir(child.path())); - ASSERT_THAT(mkdir(dir1.path().c_str(), 0777), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT( - events, - Are({Event(IN_CREATE | IN_ISDIR, child_wd, Basename(dir1.path()))})); -} - -TEST(Inotify, MultipleInotifyInstancesAndWatchesAllGetEvents) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); - constexpr int kNumFds = 30; - std::vector<FileDescriptor> inotify_fds; - - for (int i = 0; i < kNumFds; ++i) { - const DisableSave ds; // Too expensive. - inotify_fds.emplace_back( - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK))); - const FileDescriptor& fd = inotify_fds[inotify_fds.size() - 1]; // Back. - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - } - - const std::string data = "some content"; - EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), - SyscallSucceeds()); - - for (const FileDescriptor& fd : inotify_fds) { - const DisableSave ds; // Too expensive. - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - if (events.size() >= 2) { - EXPECT_EQ(events[0].mask, IN_MODIFY); - EXPECT_EQ(events[0].wd, 1); - EXPECT_EQ(events[0].name, Basename(file1.path())); - EXPECT_EQ(events[1].mask, IN_MODIFY); - EXPECT_EQ(events[1].wd, 2); - EXPECT_EQ(events[1].name, ""); - } - } -} - -TEST(Inotify, EventsGoUpAtMostOneLevel) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath dir1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS)); - const int dir1_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), dir1.path(), IN_ALL_EVENTS)); - - const std::string file1_path = file1.reset(); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_DELETE, dir1_wd, Basename(file1_path))})); -} - -TEST(Inotify, DuplicateWatchReturnsSameWatchDescriptor) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd1 = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - const int wd2 = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - EXPECT_EQ(wd1, wd2); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - // The watch shouldn't be duplicated, we only expect one event. - ASSERT_THAT(events, Are({Event(IN_OPEN, wd1)})); -} - -TEST(Inotify, UnmatchedEventsAreDiscarded) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ACCESS)); - - FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); - - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - // We only asked for access events, the open event should be discarded. - ASSERT_THAT(events, Are({})); - - // IN_IGNORED events are always generated, regardless of the mask. - file1_fd.reset(); - file1.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_IGNORED, wd)})); -} - -TEST(Inotify, AddWatchWithInvalidEventMaskFails) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - EXPECT_THAT(inotify_add_watch(fd.get(), root.path().c_str(), 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(Inotify, AddWatchOnInvalidPathFails) { - const TempPath nonexistent(NewTempAbsPath()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - // Non-existent path. - EXPECT_THAT( - inotify_add_watch(fd.get(), nonexistent.path().c_str(), IN_CREATE), - SyscallFailsWithErrno(ENOENT)); - - // Garbage path pointer. - EXPECT_THAT(inotify_add_watch(fd.get(), nullptr, IN_CREATE), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(Inotify, InOnlyDirFlagRespected) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - EXPECT_THAT( - inotify_add_watch(fd.get(), root.path().c_str(), IN_ACCESS | IN_ONLYDIR), - SyscallSucceeds()); - - EXPECT_THAT( - inotify_add_watch(fd.get(), file1.path().c_str(), IN_ACCESS | IN_ONLYDIR), - SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(Inotify, MaskAddMergesWithExistingEventMask) { - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path())); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_OPEN | IN_CLOSE_WRITE)); - - const std::string data = "some content"; - EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), - SyscallSucceeds()); - - // We shouldn't get any events, since IN_MODIFY wasn't in the event mask. - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({})); - - // Add IN_MODIFY to event mask. - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_MODIFY | IN_MASK_ADD)); - - EXPECT_THAT(write(file1_fd.get(), data.c_str(), data.length()), - SyscallSucceeds()); - - // This time we should get the modify event. - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_MODIFY, wd)})); - - // Now close the fd. If the modify event was added to the event mask rather - // than replacing the event mask we won't get the close event. - file1_fd.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events, Are({Event(IN_CLOSE_WRITE, wd)})); -} - -// Test that control events bits are not considered when checking event mask. -TEST(Inotify, ControlEvents) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), dir.path(), IN_ACCESS)); - - // Check that events in the mask are dispatched and that control bits are - // part of the event mask. - std::vector<std::string> files = - ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), false)); - ASSERT_EQ(files.size(), 2); - - const std::vector<Event> events1 = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events1, Are({Event(IN_ACCESS | IN_ISDIR, wd)})); - - // Check that events not in the mask are discarded. - const FileDescriptor dir_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); - - const std::vector<Event> events2 = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - ASSERT_THAT(events2, Are({})); -} - -// Regression test to ensure epoll and directory access doesn't deadlock. -TEST(Inotify, EpollNoDeadlock) { - const DisableSave ds; // Too many syscalls. - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - // Create lots of directories and watch all of them. - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - std::vector<TempPath> children; - for (size_t i = 0; i < 1000; ++i) { - auto child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), child.path(), IN_ACCESS)); - children.emplace_back(std::move(child)); - } - - // Run epoll_wait constantly in a separate thread. - std::atomic<bool> done(false); - ScopedThread th([&fd, &done] { - for (auto start = absl::Now(); absl::Now() - start < absl::Seconds(5);) { - FileDescriptor epoll_fd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - ASSERT_NO_ERRNO(RegisterEpollFD(epoll_fd.get(), fd.get(), - EPOLLIN | EPOLLOUT | EPOLLET, 0)); - struct epoll_event result[1]; - EXPECT_THAT(RetryEINTR(epoll_wait)(epoll_fd.get(), result, 1, -1), - SyscallSucceedsWithValue(1)); - - sched_yield(); - } - done = true; - }); - - // While epoll thread is running, constantly access all directories to - // generate inotify events. - while (!done) { - std::vector<std::string> files = - ASSERT_NO_ERRNO_AND_VALUE(ListDir(root.path(), false)); - ASSERT_EQ(files.size(), 1002); - for (const auto& child : files) { - if (child == "." || child == "..") { - continue; - } - ASSERT_NO_ERRNO_AND_VALUE(ListDir(JoinPath(root.path(), child), false)); - } - sched_yield(); - } -} - -TEST(Inotify, Fallocate) { - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); - - // Do an arbitrary modification with fallocate. - ASSERT_THAT(RetryEINTR(fallocate)(fd.get(), 0, 0, 123), SyscallSucceeds()); - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_MODIFY, wd)})); -} - -TEST(Inotify, Utimensat) { - SKIP_IF(IsRunningWithVFS1()); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); - - // Just update the access time. - struct timespec times[2] = {}; - times[0].tv_nsec = UTIME_NOW; - times[1].tv_nsec = UTIME_OMIT; - ASSERT_THAT(RetryEINTR(utimensat)(AT_FDCWD, file.path().c_str(), times, 0), - SyscallSucceeds()); - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_ACCESS, wd)})); - - // Just the modify time. - times[0].tv_nsec = UTIME_OMIT; - times[1].tv_nsec = UTIME_NOW; - ASSERT_THAT(utimensat(AT_FDCWD, file.path().c_str(), times, 0), - SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_MODIFY, wd)})); - - // Both together. - times[0].tv_nsec = UTIME_NOW; - times[1].tv_nsec = UTIME_NOW; - ASSERT_THAT(utimensat(AT_FDCWD, file.path().c_str(), times, 0), - SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); -} - -TEST(Inotify, Sendfile) { - SKIP_IF(IsRunningWithVFS1()); - - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(root.path(), "x", 0644)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - const FileDescriptor out = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Create separate inotify instances for the in and out fds. If both watches - // were on the same instance, we would have discrepancies between Linux and - // gVisor (order of events, duplicate events), which is not that important - // since inotify is asynchronous anyway. - const FileDescriptor in_inotify = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const FileDescriptor out_inotify = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int in_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(in_inotify.get(), in_file.path(), IN_ALL_EVENTS)); - const int out_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(out_inotify.get(), out_file.path(), IN_ALL_EVENTS)); - - ASSERT_THAT(sendfile(out.get(), in.get(), /*offset=*/nullptr, 1), - SyscallSucceeds()); - - // Expect a single access event and a single modify event. - std::vector<Event> in_events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(in_inotify.get())); - std::vector<Event> out_events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(out_inotify.get())); - EXPECT_THAT(in_events, Are({Event(IN_ACCESS, in_wd)})); - EXPECT_THAT(out_events, Are({Event(IN_MODIFY, out_wd)})); -} - -TEST(Inotify, SpliceOnWatchTarget) { - int pipefds[2]; - ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds()); - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - dir.path(), "some content", TempPath::kDefaultFileMode)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), dir.path(), IN_ALL_EVENTS)); - const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); - - EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr, 1, /*flags=*/0), - SyscallSucceedsWithValue(1)); - - // Surprisingly, events may not be generated in Linux if we read from a file. - // fs/splice.c:generic_file_splice_read, which is used most often, does not - // generate events, whereas fs/splice.c:default_file_splice_read does. - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - if (IsRunningOnGvisor() && !IsRunningWithVFS1()) { - ASSERT_THAT(events, Are({Event(IN_ACCESS, dir_wd, Basename(file.path())), - Event(IN_ACCESS, file_wd)})); - } - - EXPECT_THAT(splice(pipefds[0], nullptr, fd.get(), nullptr, 1, /*flags=*/0), - SyscallSucceedsWithValue(1)); - - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - ASSERT_THAT(events, Are({ - Event(IN_MODIFY, dir_wd, Basename(file.path())), - Event(IN_MODIFY, file_wd), - })); -} - -TEST(Inotify, SpliceOnInotifyFD) { - int pipefds[2]; - ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds()); - - const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - root.path(), "some content", TempPath::kDefaultFileMode)); - - const FileDescriptor file1_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY)); - const int watcher = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS)); - - char buf; - EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - - EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr, - sizeof(struct inotify_event) + 1, SPLICE_F_NONBLOCK), - SyscallSucceedsWithValue(sizeof(struct inotify_event))); - - const FileDescriptor read_fd(pipefds[0]); - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(read_fd.get())); - ASSERT_THAT(events, Are({Event(IN_ACCESS, watcher)})); -} - -// Watches on a parent should not be triggered by actions on a hard link to one -// of its children that has a different parent. -TEST(Inotify, LinkOnOtherParent) { - // Inotify does not work properly with hard links in gofer and overlay fs. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); - - const TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - std::string link_path = NewTempAbsPathInDir(dir2.path()); - - ASSERT_THAT(link(file.path().c_str(), link_path.c_str()), SyscallSucceeds()); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), dir1.path(), IN_ALL_EVENTS)); - - // Perform various actions on the link outside of dir1, which should trigger - // no inotify events. - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(link_path.c_str(), O_RDWR)); - int val = 0; - ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds()); - ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds()); - ASSERT_THAT(ftruncate(fd.get(), 12345), SyscallSucceeds()); - - // Close before unlinking; some filesystems cannot restore an open fd on an - // unlinked file. - fd.reset(); - ASSERT_THAT(unlink(link_path.c_str()), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({})); -} - -TEST(Inotify, Xattr) { - // TODO(gvisor.dev/issue/1636): Support extended attributes in runsc gofer. - SKIP_IF(IsRunningOnGvisor()); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string path = file.path(); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDWR)); - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), path, IN_ALL_EVENTS)); - - const char* cpath = path.c_str(); - const char* name = "user.test"; - int val = 123; - ASSERT_THAT(setxattr(cpath, name, &val, sizeof(val), /*flags=*/0), - SyscallSucceeds()); - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); - - ASSERT_THAT(getxattr(cpath, name, &val, sizeof(val)), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({})); - - char list[100]; - ASSERT_THAT(listxattr(cpath, list, sizeof(list)), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({})); - - ASSERT_THAT(removexattr(cpath, name), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); - - ASSERT_THAT(fsetxattr(fd.get(), name, &val, sizeof(val), /*flags=*/0), - SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); - - ASSERT_THAT(fgetxattr(fd.get(), name, &val, sizeof(val)), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({})); - - ASSERT_THAT(flistxattr(fd.get(), list, sizeof(list)), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({})); - - ASSERT_THAT(fremovexattr(fd.get(), name), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({Event(IN_ATTRIB, wd)})); -} - -TEST(Inotify, Exec) { - SKIP_IF(IsRunningWithVFS1()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), "/bin/true", IN_ALL_EVENTS)); - - // Perform exec. - pid_t child = -1; - int execve_errno = -1; - auto kill = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/bin/true", {}, {}, nullptr, &child, &execve_errno)); - ASSERT_EQ(0, execve_errno); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_EQ(0, status); - - // Process cleanup no longer needed. - kill.Release(); - - std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd), - Event(IN_CLOSE_NOWRITE, wd)})); -} - -// Watches without IN_EXCL_UNLINK, should continue to emit events for file -// descriptors after their corresponding files have been unlinked. -// -// We need to disable S/R because there are filesystems where we cannot re-open -// fds to an unlinked file across S/R, e.g. gofer-backed filesytems. -TEST(Inotify, IncludeUnlinkedFile_NoRandomSave) { - const DisableSave ds; - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(dir.path(), "123", TempPath::kDefaultFileMode)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), dir.path(), IN_ALL_EVENTS)); - const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); - - ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); - int val = 0; - ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds()); - ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds()); - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, AnyOf(Are({ - Event(IN_ATTRIB, file_wd), - Event(IN_DELETE, dir_wd, Basename(file.path())), - Event(IN_ACCESS, dir_wd, Basename(file.path())), - Event(IN_ACCESS, file_wd), - Event(IN_MODIFY, dir_wd, Basename(file.path())), - Event(IN_MODIFY, file_wd), - }), - Are({ - Event(IN_DELETE, dir_wd, Basename(file.path())), - Event(IN_ATTRIB, file_wd), - Event(IN_ACCESS, dir_wd, Basename(file.path())), - Event(IN_ACCESS, file_wd), - Event(IN_MODIFY, dir_wd, Basename(file.path())), - Event(IN_MODIFY, file_wd), - }))); - - fd.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_CLOSE_WRITE, dir_wd, Basename(file.path())), - Event(IN_CLOSE_WRITE, file_wd), - Event(IN_DELETE_SELF, file_wd), - Event(IN_IGNORED, file_wd), - })); -} - -// Watches created with IN_EXCL_UNLINK will stop emitting events on fds for -// children that have already been unlinked. -// -// We need to disable S/R because there are filesystems where we cannot re-open -// fds to an unlinked file across S/R, e.g. gofer-backed filesytems. -TEST(Inotify, ExcludeUnlink_NoRandomSave) { - const DisableSave ds; - // TODO(gvisor.dev/issue/1624): This test fails on VFS1. - SKIP_IF(IsRunningWithVFS1()); - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - const int file_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), file.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - - // Unlink the child, which should cause further operations on the open file - // descriptor to be ignored. - ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); - int val = 0; - ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds()); - ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds()); - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, AreUnordered({ - Event(IN_ATTRIB, file_wd), - Event(IN_DELETE, dir_wd, Basename(file.path())), - })); - - fd.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - ASSERT_THAT(events, Are({ - Event(IN_DELETE_SELF, file_wd), - Event(IN_IGNORED, file_wd), - })); -} - -// We need to disable S/R because there are filesystems where we cannot re-open -// fds to an unlinked file across S/R, e.g. gofer-backed filesytems. -TEST(Inotify, ExcludeUnlinkDirectory_NoRandomSave) { - // TODO(gvisor.dev/issue/1624): This test fails on VFS1. Remove once VFS1 is - // deleted. - SKIP_IF(IsRunningWithVFS1()); - - const DisableSave ds; - - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath dir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent.path())); - std::string dirPath = dir.path(); - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dirPath.c_str(), O_RDONLY | O_DIRECTORY)); - const int parent_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), parent.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - const int self_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - - // Unlink the dir, and then close the open fd. - ASSERT_THAT(rmdir(dirPath.c_str()), SyscallSucceeds()); - dir.reset(); - - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - // No close event should appear. - ASSERT_THAT(events, - Are({Event(IN_DELETE | IN_ISDIR, parent_wd, Basename(dirPath))})); - - fd.reset(); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - ASSERT_THAT(events, Are({ - Event(IN_DELETE_SELF, self_wd), - Event(IN_IGNORED, self_wd), - })); -} - -// If "dir/child" and "dir/child2" are links to the same file, and "dir/child" -// is unlinked, a watch on "dir" with IN_EXCL_UNLINK will exclude future events -// for fds on "dir/child" but not "dir/child2". -// -// We need to disable S/R because there are filesystems where we cannot re-open -// fds to an unlinked file across S/R, e.g. gofer-backed filesytems. -TEST(Inotify, ExcludeUnlinkMultipleChildren_NoRandomSave) { - // Inotify does not work properly with hard links in gofer and overlay fs. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); - // TODO(gvisor.dev/issue/1624): This test fails on VFS1. - SKIP_IF(IsRunningWithVFS1()); - - const DisableSave ds; - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - std::string path1 = file.path(); - std::string path2 = NewTempAbsPathInDir(dir.path()); - ASSERT_THAT(link(path1.c_str(), path2.c_str()), SyscallSucceeds()); - - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(path1.c_str(), O_RDWR)); - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open(path2.c_str(), O_RDWR)); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - - // After unlinking path1, only events on the fd for path2 should be generated. - ASSERT_THAT(unlink(path1.c_str()), SyscallSucceeds()); - ASSERT_THAT(write(fd1.get(), "x", 1), SyscallSucceeds()); - ASSERT_THAT(write(fd2.get(), "x", 1), SyscallSucceeds()); - - const std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_DELETE, wd, Basename(path1)), - Event(IN_MODIFY, wd, Basename(path2)), - })); -} - -// On native Linux, actions of data type FSNOTIFY_EVENT_INODE are not affected -// by IN_EXCL_UNLINK (see -// fs/notify/inotify/inotify_fsnotify.c:inotify_handle_event). Inode-level -// events include changes to metadata and extended attributes. -// -// We need to disable S/R because there are filesystems where we cannot re-open -// fds to an unlinked file across S/R, e.g. gofer-backed filesytems. -TEST(Inotify, ExcludeUnlinkInodeEvents_NoRandomSave) { - // TODO(gvisor.dev/issue/1624): Fails on VFS1. - SKIP_IF(IsRunningWithVFS1()); - - // NOTE(gvisor.dev/issue/3654): In the gofer filesystem, we do not allow - // setting attributes through an fd if the file at the open path has been - // deleted. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir()))); - - const DisableSave ds; - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_RDWR)); - - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), dir.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - const int file_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch( - inotify_fd.get(), file.path(), IN_ALL_EVENTS | IN_EXCL_UNLINK)); - - // Even after unlinking, inode-level operations will trigger events regardless - // of IN_EXCL_UNLINK. - ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); - - // Perform various actions on fd. - ASSERT_THAT(ftruncate(fd.get(), 12345), SyscallSucceeds()); - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, AnyOf(Are({ - Event(IN_ATTRIB, file_wd), - Event(IN_DELETE, dir_wd, Basename(file.path())), - Event(IN_MODIFY, dir_wd, Basename(file.path())), - Event(IN_MODIFY, file_wd), - }), - Are({ - Event(IN_DELETE, dir_wd, Basename(file.path())), - Event(IN_ATTRIB, file_wd), - Event(IN_MODIFY, dir_wd, Basename(file.path())), - Event(IN_MODIFY, file_wd), - }))); - - const struct timeval times[2] = {{1, 0}, {2, 0}}; - ASSERT_THAT(futimes(fd.get(), times), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_ATTRIB, dir_wd, Basename(file.path())), - Event(IN_ATTRIB, file_wd), - })); - - // S/R is disabled on this entire test due to behavior with unlink; it must - // also be disabled after this point because of fchmod. - ASSERT_THAT(fchmod(fd.get(), 0777), SyscallSucceeds()); - events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_ATTRIB, dir_wd, Basename(file.path())), - Event(IN_ATTRIB, file_wd), - })); -} - -TEST(Inotify, OneShot) { - // TODO(gvisor.dev/issue/1624): IN_ONESHOT not supported in VFS1. - SKIP_IF(IsRunningWithVFS1()); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor inotify_fd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - - const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(inotify_fd.get(), file.path(), IN_MODIFY | IN_ONESHOT)); - - // Open an fd, write to it, and then close it. - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - ASSERT_THAT(write(fd.get(), "x", 1), SyscallSucceedsWithValue(1)); - fd.reset(); - - // We should get a single event followed by IN_IGNORED indicating removal - // of the one-shot watch. Prior activity (i.e. open) that is not in the mask - // should not trigger removal, and activity after removal (i.e. close) should - // not generate events. - std::vector<Event> events = - ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - EXPECT_THAT(events, Are({ - Event(IN_MODIFY, wd), - Event(IN_IGNORED, wd), - })); - - // The watch should already have been removed. - EXPECT_THAT(inotify_rm_watch(inotify_fd.get(), wd), - SyscallFailsWithErrno(EINVAL)); -} - -// This test helps verify that the lock order of filesystem and inotify locks -// is respected when inotify instances and watch targets are concurrently being -// destroyed. -TEST(InotifyTest, InotifyAndTargetDestructionDoNotDeadlock_NoRandomSave) { - const DisableSave ds; // Too many syscalls. - - // A file descriptor protected by a mutex. This ensures that while a - // descriptor is in use, it cannot be closed and reused for a different file - // description. - struct atomic_fd { - int fd; - absl::Mutex mu; - }; - - // Set up initial inotify instances. - constexpr int num_fds = 3; - std::vector<atomic_fd> fds(num_fds); - for (int i = 0; i < num_fds; i++) { - int fd; - ASSERT_THAT(fd = inotify_init1(IN_NONBLOCK), SyscallSucceeds()); - fds[i].fd = fd; - } - - // Set up initial watch targets. - std::vector<std::string> paths; - for (int i = 0; i < 3; i++) { - paths.push_back(NewTempAbsPath()); - ASSERT_THAT(mknod(paths[i].c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); - } - - constexpr absl::Duration runtime = absl::Seconds(4); - - // Constantly replace each inotify instance with a new one. - auto replace_fds = [&] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - for (auto& afd : fds) { - int new_fd; - ASSERT_THAT(new_fd = inotify_init1(IN_NONBLOCK), SyscallSucceeds()); - absl::MutexLock l(&afd.mu); - ASSERT_THAT(close(afd.fd), SyscallSucceeds()); - afd.fd = new_fd; - for (auto& p : paths) { - // inotify_add_watch may fail if the file at p was deleted. - ASSERT_THAT(inotify_add_watch(afd.fd, p.c_str(), IN_ALL_EVENTS), - AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT))); - } - } - sched_yield(); - } - }; - - std::list<ScopedThread> ts; - for (int i = 0; i < 3; i++) { - ts.emplace_back(replace_fds); - } - - // Constantly replace each watch target with a new one. - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - for (auto& p : paths) { - ASSERT_THAT(unlink(p.c_str()), SyscallSucceeds()); - ASSERT_THAT(mknod(p.c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); - } - sched_yield(); - } -} - -// This test helps verify that the lock order of filesystem and inotify locks -// is respected when adding/removing watches occurs concurrently with the -// removal of their targets. -TEST(InotifyTest, AddRemoveUnlinkDoNotDeadlock_NoRandomSave) { - const DisableSave ds; // Too many syscalls. - - // Set up inotify instances. - constexpr int num_fds = 3; - std::vector<int> fds(num_fds); - for (int i = 0; i < num_fds; i++) { - ASSERT_THAT(fds[i] = inotify_init1(IN_NONBLOCK), SyscallSucceeds()); - } - - // Set up initial watch targets. - std::vector<std::string> paths; - for (int i = 0; i < 3; i++) { - paths.push_back(NewTempAbsPath()); - ASSERT_THAT(mknod(paths[i].c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); - } - - constexpr absl::Duration runtime = absl::Seconds(1); - - // Constantly add/remove watches for each inotify instance/watch target pair. - auto add_remove_watches = [&] { - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - for (int fd : fds) { - for (auto& p : paths) { - // Do not assert on inotify_add_watch and inotify_rm_watch. They may - // fail if the file at p was deleted. inotify_add_watch may also fail - // if another thread beat us to adding a watch. - const int wd = inotify_add_watch(fd, p.c_str(), IN_ALL_EVENTS); - if (wd > 0) { - inotify_rm_watch(fd, wd); - } - } - } - sched_yield(); - } - }; - - std::list<ScopedThread> ts; - for (int i = 0; i < 15; i++) { - ts.emplace_back(add_remove_watches); - } - - // Constantly replace each watch target with a new one. - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - for (auto& p : paths) { - ASSERT_THAT(unlink(p.c_str()), SyscallSucceeds()); - ASSERT_THAT(mknod(p.c_str(), S_IFREG | 0600, 0), SyscallSucceeds()); - } - sched_yield(); - } -} - -// This test helps verify that the lock order of filesystem and inotify locks -// is respected when many inotify events and filesystem operations occur -// simultaneously. -TEST(InotifyTest, NotifyNoDeadlock_NoRandomSave) { - const DisableSave ds; // Too many syscalls. - - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string dir = parent.path(); - - // mu protects file, which will change on rename. - absl::Mutex mu; - std::string file = NewTempAbsPathInDir(dir); - ASSERT_THAT(mknod(file.c_str(), 0644 | S_IFREG, 0), SyscallSucceeds()); - - const absl::Duration runtime = absl::Milliseconds(300); - - // Add/remove watches on dir and file. - ScopedThread add_remove_watches([&] { - const FileDescriptor ifd = - ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); - int dir_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(ifd.get(), dir, IN_ALL_EVENTS)); - int file_wd; - { - absl::ReaderMutexLock l(&mu); - file_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(ifd.get(), file, IN_ALL_EVENTS)); - } - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - ASSERT_THAT(inotify_rm_watch(ifd.get(), file_wd), SyscallSucceeds()); - ASSERT_THAT(inotify_rm_watch(ifd.get(), dir_wd), SyscallSucceeds()); - dir_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(ifd.get(), dir, IN_ALL_EVENTS)); - { - absl::ReaderMutexLock l(&mu); - file_wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(ifd.get(), file, IN_ALL_EVENTS)); - } - sched_yield(); - } - }); - - // Modify attributes on dir and file. - ScopedThread stats([&] { - int fd, dir_fd; - { - absl::ReaderMutexLock l(&mu); - ASSERT_THAT(fd = open(file.c_str(), O_RDONLY), SyscallSucceeds()); - } - ASSERT_THAT(dir_fd = open(dir.c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - const struct timeval times[2] = {{1, 0}, {2, 0}}; - - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - { - absl::ReaderMutexLock l(&mu); - EXPECT_THAT(utimes(file.c_str(), times), SyscallSucceeds()); - } - EXPECT_THAT(futimes(fd, times), SyscallSucceeds()); - EXPECT_THAT(utimes(dir.c_str(), times), SyscallSucceeds()); - EXPECT_THAT(futimes(dir_fd, times), SyscallSucceeds()); - sched_yield(); - } - }); - - // Modify extended attributes on dir and file. - ScopedThread xattrs([&] { - // TODO(gvisor.dev/issue/1636): Support extended attributes in runsc gofer. - if (!IsRunningOnGvisor()) { - int fd; - { - absl::ReaderMutexLock l(&mu); - ASSERT_THAT(fd = open(file.c_str(), O_RDONLY), SyscallSucceeds()); - } - - const char* name = "user.test"; - int val = 123; - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - { - absl::ReaderMutexLock l(&mu); - ASSERT_THAT( - setxattr(file.c_str(), name, &val, sizeof(val), /*flags=*/0), - SyscallSucceeds()); - ASSERT_THAT(removexattr(file.c_str(), name), SyscallSucceeds()); - } - - ASSERT_THAT(fsetxattr(fd, name, &val, sizeof(val), /*flags=*/0), - SyscallSucceeds()); - ASSERT_THAT(fremovexattr(fd, name), SyscallSucceeds()); - sched_yield(); - } - } - }); - - // Read and write file's contents. Read and write dir's entries. - ScopedThread read_write([&] { - int fd; - { - absl::ReaderMutexLock l(&mu); - ASSERT_THAT(fd = open(file.c_str(), O_RDWR), SyscallSucceeds()); - } - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - int val = 123; - ASSERT_THAT(write(fd, &val, sizeof(val)), SyscallSucceeds()); - ASSERT_THAT(read(fd, &val, sizeof(val)), SyscallSucceeds()); - TempPath new_file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir)); - ASSERT_NO_ERRNO(ListDir(dir, false)); - new_file.reset(); - sched_yield(); - } - }); - - // Rename file. - for (auto start = absl::Now(); absl::Now() - start < runtime;) { - const std::string new_path = NewTempAbsPathInDir(dir); - { - absl::WriterMutexLock l(&mu); - ASSERT_THAT(rename(file.c_str(), new_path.c_str()), SyscallSucceeds()); - file = new_path; - } - sched_yield(); - } -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ioctl.cc b/test/syscalls/linux/ioctl.cc deleted file mode 100644 index 9b16d1558..000000000 --- a/test/syscalls/linux/ioctl.cc +++ /dev/null @@ -1,419 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <errno.h> -#include <fcntl.h> -#include <net/if.h> -#include <netdb.h> -#include <signal.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.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/signal_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -bool CheckNonBlocking(int fd) { - int ret = fcntl(fd, F_GETFL, 0); - TEST_CHECK(ret != -1); - return (ret & O_NONBLOCK) == O_NONBLOCK; -} - -bool CheckCloExec(int fd) { - int ret = fcntl(fd, F_GETFD, 0); - TEST_CHECK(ret != -1); - return (ret & FD_CLOEXEC) == FD_CLOEXEC; -} - -class IoctlTest : public ::testing::Test { - protected: - void SetUp() override { - ASSERT_THAT(fd_ = open("/dev/null", O_RDONLY), SyscallSucceeds()); - } - - void TearDown() override { - if (fd_ >= 0) { - ASSERT_THAT(close(fd_), SyscallSucceeds()); - fd_ = -1; - } - } - - int fd() const { return fd_; } - - private: - int fd_ = -1; -}; - -TEST_F(IoctlTest, BadFileDescriptor) { - EXPECT_THAT(ioctl(-1 /* fd */, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(IoctlTest, InvalidControlNumber) { - EXPECT_THAT(ioctl(STDOUT_FILENO, 0), SyscallFailsWithErrno(ENOTTY)); -} - -TEST_F(IoctlTest, IoctlWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_PATH)); - - int set = 1; - EXPECT_THAT(ioctl(fd.get(), FIONBIO, &set), SyscallFailsWithErrno(EBADF)); - - EXPECT_THAT(ioctl(fd.get(), FIONCLEX), SyscallFailsWithErrno(EBADF)); - - EXPECT_THAT(ioctl(fd.get(), FIOCLEX), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(IoctlTest, FIONBIOSucceeds) { - EXPECT_FALSE(CheckNonBlocking(fd())); - int set = 1; - EXPECT_THAT(ioctl(fd(), FIONBIO, &set), SyscallSucceeds()); - EXPECT_TRUE(CheckNonBlocking(fd())); - set = 0; - EXPECT_THAT(ioctl(fd(), FIONBIO, &set), SyscallSucceeds()); - EXPECT_FALSE(CheckNonBlocking(fd())); -} - -TEST_F(IoctlTest, FIONBIOFails) { - EXPECT_THAT(ioctl(fd(), FIONBIO, nullptr), SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(IoctlTest, FIONCLEXSucceeds) { - EXPECT_THAT(ioctl(fd(), FIONCLEX), SyscallSucceeds()); - EXPECT_FALSE(CheckCloExec(fd())); -} - -TEST_F(IoctlTest, FIOCLEXSucceeds) { - EXPECT_THAT(ioctl(fd(), FIOCLEX), SyscallSucceeds()); - EXPECT_TRUE(CheckCloExec(fd())); -} - -TEST_F(IoctlTest, FIOASYNCFails) { - EXPECT_THAT(ioctl(fd(), FIOASYNC, nullptr), SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(IoctlTest, FIOASYNCSucceeds) { - // Not all FDs support FIOASYNC. - const FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int before = -1; - ASSERT_THAT(before = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - - int set = 1; - EXPECT_THAT(ioctl(s.get(), FIOASYNC, &set), SyscallSucceeds()); - - int after_set = -1; - ASSERT_THAT(after_set = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - EXPECT_EQ(after_set, before | O_ASYNC) << "before was " << before; - - set = 0; - EXPECT_THAT(ioctl(s.get(), FIOASYNC, &set), SyscallSucceeds()); - - ASSERT_THAT(fcntl(s.get(), F_GETFL), SyscallSucceedsWithValue(before)); -} - -/* Count of the number of SIGIOs handled. */ -static volatile int io_received = 0; - -void inc_io_handler(int sig, siginfo_t* siginfo, void* arg) { io_received++; } - -TEST_F(IoctlTest, FIOASYNCNoTarget) { - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - // Count SIGIOs received. - io_received = 0; - struct sigaction sa; - sa.sa_sigaction = inc_io_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - // Actually allow SIGIO delivery. - auto mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); - - int set = 1; - EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); - - constexpr char kData[] = "abc"; - ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), - SyscallSucceedsWithValue(sizeof(kData))); - - EXPECT_EQ(io_received, 0); -} - -TEST_F(IoctlTest, FIOASYNCSelfTarget) { - // FIXME(b/120624367): gVisor erroneously sends SIGIO on close(2), which would - // kill the test when pair goes out of scope. Temporarily ignore SIGIO so that - // that the close signal is ignored. - struct sigaction sa; - sa.sa_handler = SIG_IGN; - auto early_sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - // Count SIGIOs received. - io_received = 0; - sa.sa_sigaction = inc_io_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - // Actually allow SIGIO delivery. - auto mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); - - int set = 1; - EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); - - pid_t pid = getpid(); - EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); - - constexpr char kData[] = "abc"; - ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), - SyscallSucceedsWithValue(sizeof(kData))); - - EXPECT_EQ(io_received, 1); -} - -// Equivalent to FIOASYNCSelfTarget except that FIOSETOWN is called before -// FIOASYNC. -TEST_F(IoctlTest, FIOASYNCSelfTarget2) { - // FIXME(b/120624367): gVisor erroneously sends SIGIO on close(2), which would - // kill the test when pair goes out of scope. Temporarily ignore SIGIO so that - // that the close signal is ignored. - struct sigaction sa; - sa.sa_handler = SIG_IGN; - auto early_sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - // Count SIGIOs received. - io_received = 0; - sa.sa_sigaction = inc_io_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - // Actually allow SIGIO delivery. - auto mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); - - pid_t pid = -1; - EXPECT_THAT(pid = getpid(), SyscallSucceeds()); - EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); - - int set = 1; - EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); - - constexpr char kData[] = "abc"; - ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), - SyscallSucceedsWithValue(sizeof(kData))); - - EXPECT_EQ(io_received, 1); -} - -// Check that closing an FD does not result in an event. -TEST_F(IoctlTest, FIOASYNCSelfTargetClose) { - // Count SIGIOs received. - struct sigaction sa; - io_received = 0; - sa.sa_sigaction = inc_io_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - // Actually allow SIGIO delivery. - auto mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); - - for (int i = 0; i < 2; i++) { - auto pair = ASSERT_NO_ERRNO_AND_VALUE( - UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - pid_t pid = getpid(); - EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); - - int set = 1; - EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); - } - - // FIXME(b/120624367): gVisor erroneously sends SIGIO on close. - SKIP_IF(IsRunningOnGvisor()); - - EXPECT_EQ(io_received, 0); -} - -TEST_F(IoctlTest, FIOASYNCInvalidPID) { - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - int set = 1; - ASSERT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); - pid_t pid = INT_MAX; - // This succeeds (with behavior equivalent to a pid of 0) in Linux prior to - // f73127356f34 "fs/fcntl: return -ESRCH in f_setown when pid/pgid can't be - // found", and fails with EPERM after that commit. - EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), - AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ESRCH))); -} - -TEST_F(IoctlTest, FIOASYNCUnsetTarget) { - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - // Count SIGIOs received. - io_received = 0; - struct sigaction sa; - sa.sa_sigaction = inc_io_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART; - auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGIO, sa)); - - // Actually allow SIGIO delivery. - auto mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGIO)); - - int set = 1; - EXPECT_THAT(ioctl(pair->second_fd(), FIOASYNC, &set), SyscallSucceeds()); - - pid_t pid = getpid(); - EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); - - // Passing a PID of 0 unsets the target. - pid = 0; - EXPECT_THAT(ioctl(pair->second_fd(), FIOSETOWN, &pid), SyscallSucceeds()); - - constexpr char kData[] = "abc"; - ASSERT_THAT(WriteFd(pair->first_fd(), kData, sizeof(kData)), - SyscallSucceedsWithValue(sizeof(kData))); - - EXPECT_EQ(io_received, 0); -} - -using IoctlTestSIOCGIFCONF = SimpleSocketTest; - -TEST_P(IoctlTestSIOCGIFCONF, ValidateNoArrayGetsLength) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Validate that no array can be used to get the length required. - struct ifconf ifconf = {}; - ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); - ASSERT_GT(ifconf.ifc_len, 0); -} - -// This test validates that we will only return a partial array list and not -// partial ifrreq structs. -TEST_P(IoctlTestSIOCGIFCONF, ValidateNoPartialIfrsReturned) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - struct ifreq ifr = {}; - struct ifconf ifconf = {}; - ifconf.ifc_len = sizeof(ifr) - 1; // One byte too few. - ifconf.ifc_ifcu.ifcu_req = 𝔦 - - ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); - ASSERT_EQ(ifconf.ifc_len, 0); - ASSERT_EQ(ifr.ifr_name[0], '\0'); // Nothing is returned. - - ifconf.ifc_len = sizeof(ifreq); - ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); - ASSERT_GT(ifconf.ifc_len, 0); - ASSERT_NE(ifr.ifr_name[0], '\0'); // An interface can now be returned. -} - -TEST_P(IoctlTestSIOCGIFCONF, ValidateLoopbackIsPresent) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - struct ifconf ifconf = {}; - struct ifreq ifr[10] = {}; // Storage for up to 10 interfaces. - - ifconf.ifc_req = ifr; - ifconf.ifc_len = sizeof(ifr); - - ASSERT_THAT(ioctl(fd->get(), SIOCGIFCONF, &ifconf), SyscallSucceeds()); - size_t num_if = ifconf.ifc_len / sizeof(struct ifreq); - - // We should have at least one interface. - ASSERT_GE(num_if, 1); - - // One of the interfaces should be a loopback. - bool found_loopback = false; - for (size_t i = 0; i < num_if; ++i) { - if (strcmp(ifr[i].ifr_name, "lo") == 0) { - // SIOCGIFCONF returns the ipv4 address of the interface, let's check it. - ASSERT_EQ(ifr[i].ifr_addr.sa_family, AF_INET); - - // Validate the address is correct for loopback. - sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&ifr[i].ifr_addr); - ASSERT_EQ(htonl(sin->sin_addr.s_addr), INADDR_LOOPBACK); - - found_loopback = true; - break; - } - } - ASSERT_TRUE(found_loopback); -} - -std::vector<SocketKind> IoctlSocketTypes() { - return {SimpleSocket(AF_UNIX, SOCK_STREAM, 0), - SimpleSocket(AF_UNIX, SOCK_DGRAM, 0), - SimpleSocket(AF_INET, SOCK_STREAM, 0), - SimpleSocket(AF_INET6, SOCK_STREAM, 0), - SimpleSocket(AF_INET, SOCK_DGRAM, 0), - SimpleSocket(AF_INET6, SOCK_DGRAM, 0)}; -} - -INSTANTIATE_TEST_SUITE_P(IoctlTest, IoctlTestSIOCGIFCONF, - ::testing::ValuesIn(IoctlSocketTypes())); - -} // namespace - -TEST_F(IoctlTest, FIOGETOWNSucceeds) { - const FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int get = -1; - ASSERT_THAT(ioctl(s.get(), FIOGETOWN, &get), SyscallSucceeds()); - EXPECT_EQ(get, 0); -} - -TEST_F(IoctlTest, SIOCGPGRPSucceeds) { - const FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); - - int get = -1; - ASSERT_THAT(ioctl(s.get(), SIOCGPGRP, &get), SyscallSucceeds()); - EXPECT_EQ(get, 0); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ip6tables.cc b/test/syscalls/linux/ip6tables.cc deleted file mode 100644 index e0e146067..000000000 --- a/test/syscalls/linux/ip6tables.cc +++ /dev/null @@ -1,233 +0,0 @@ -// 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 <linux/capability.h> -#include <sys/socket.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/iptables.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr char kNatTablename[] = "nat"; -constexpr char kErrorTarget[] = "ERROR"; -constexpr size_t kEmptyStandardEntrySize = - sizeof(struct ip6t_entry) + sizeof(struct xt_standard_target); -constexpr size_t kEmptyErrorEntrySize = - sizeof(struct ip6t_entry) + sizeof(struct xt_error_target); - -TEST(IP6TablesBasic, FailSockoptNonRaw) { - // Even if the user has CAP_NET_RAW, they shouldn't be able to use the - // ip6tables sockopts with a non-raw socket. - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_DGRAM, 0), SyscallSucceeds()); - - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info); - EXPECT_THAT(getsockopt(sock, SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size), - SyscallFailsWithErrno(ENOPROTOOPT)); - - EXPECT_THAT(close(sock), SyscallSucceeds()); -} - -TEST(IP6TablesBasic, GetInfoErrorPrecedence) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_DGRAM, 0), SyscallSucceeds()); - - // When using the wrong type of socket and a too-short optlen, we should get - // EINVAL. - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info) - 1; - EXPECT_THAT(getsockopt(sock, SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(IP6TablesBasic, GetEntriesErrorPrecedence) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_DGRAM, 0), SyscallSucceeds()); - - // When using the wrong type of socket and a too-short optlen, we should get - // EINVAL. - struct ip6t_get_entries entries = {}; - socklen_t entries_size = sizeof(struct ip6t_get_entries) - 1; - snprintf(entries.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - EXPECT_THAT( - getsockopt(sock, SOL_IPV6, IP6T_SO_GET_ENTRIES, &entries, &entries_size), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(IP6TablesBasic, GetRevision) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW), - SyscallSucceeds()); - - struct xt_get_revision rev = {}; - socklen_t rev_len = sizeof(rev); - - snprintf(rev.name, sizeof(rev.name), "REDIRECT"); - rev.revision = 0; - - // Revision 0 exists. - EXPECT_THAT( - getsockopt(sock, SOL_IPV6, IP6T_SO_GET_REVISION_TARGET, &rev, &rev_len), - SyscallSucceeds()); - EXPECT_EQ(rev.revision, 0); - - // Revisions > 0 don't exist. - rev.revision = 1; - EXPECT_THAT( - getsockopt(sock, SOL_IPV6, IP6T_SO_GET_REVISION_TARGET, &rev, &rev_len), - SyscallFailsWithErrno(EPROTONOSUPPORT)); -} - -// This tests the initial state of a machine with empty ip6tables via -// getsockopt(IP6T_SO_GET_INFO). We don't have a guarantee that the iptables are -// empty when running in native, but we can test that gVisor has the same -// initial state that a newly-booted Linux machine would have. -TEST(IP6TablesTest, InitialInfo) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)); - - // Get info via sockopt. - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info); - ASSERT_THAT( - getsockopt(sock.get(), SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size), - SyscallSucceeds()); - - // The nat table supports PREROUTING, and OUTPUT. - unsigned int valid_hooks = - (1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_OUT) | - (1 << NF_IP6_POST_ROUTING) | (1 << NF_IP6_LOCAL_IN); - EXPECT_EQ(info.valid_hooks, valid_hooks); - - // Each chain consists of an empty entry with a standard target.. - EXPECT_EQ(info.hook_entry[NF_IP6_PRE_ROUTING], 0); - EXPECT_EQ(info.hook_entry[NF_IP6_LOCAL_IN], kEmptyStandardEntrySize); - EXPECT_EQ(info.hook_entry[NF_IP6_LOCAL_OUT], kEmptyStandardEntrySize * 2); - EXPECT_EQ(info.hook_entry[NF_IP6_POST_ROUTING], kEmptyStandardEntrySize * 3); - - // The underflow points are the same as the entry points. - EXPECT_EQ(info.underflow[NF_IP6_PRE_ROUTING], 0); - EXPECT_EQ(info.underflow[NF_IP6_LOCAL_IN], kEmptyStandardEntrySize); - EXPECT_EQ(info.underflow[NF_IP6_LOCAL_OUT], kEmptyStandardEntrySize * 2); - EXPECT_EQ(info.underflow[NF_IP6_POST_ROUTING], kEmptyStandardEntrySize * 3); - - // One entry for each chain, plus an error entry at the end. - EXPECT_EQ(info.num_entries, 5); - - EXPECT_EQ(info.size, 4 * kEmptyStandardEntrySize + kEmptyErrorEntrySize); - EXPECT_EQ(strcmp(info.name, kNatTablename), 0); -} - -// This tests the initial state of a machine with empty ip6tables via -// getsockopt(IP6T_SO_GET_ENTRIES). We don't have a guarantee that the iptables -// are empty when running in native, but we can test that gVisor has the same -// initial state that a newly-booted Linux machine would have. -TEST(IP6TablesTest, InitialEntries) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_RAW, IPPROTO_RAW)); - - // Get info via sockopt. - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info); - ASSERT_THAT( - getsockopt(sock.get(), SOL_IPV6, IP6T_SO_GET_INFO, &info, &info_size), - SyscallSucceeds()); - - // Use info to get entries. - socklen_t entries_size = sizeof(struct ip6t_get_entries) + info.size; - struct ip6t_get_entries* entries = - static_cast<struct ip6t_get_entries*>(malloc(entries_size)); - snprintf(entries->name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - entries->size = info.size; - ASSERT_THAT(getsockopt(sock.get(), SOL_IPV6, IP6T_SO_GET_ENTRIES, entries, - &entries_size), - SyscallSucceeds()); - - // Verify the name and size. - ASSERT_EQ(info.size, entries->size); - ASSERT_EQ(strcmp(entries->name, kNatTablename), 0); - - // Verify that the entrytable is 4 entries with accept targets and no matches - // followed by a single error target. - size_t entry_offset = 0; - while (entry_offset < entries->size) { - struct ip6t_entry* entry = reinterpret_cast<struct ip6t_entry*>( - reinterpret_cast<char*>(entries->entrytable) + entry_offset); - - // ipv6 should be zeroed. - struct ip6t_ip6 zeroed = {}; - ASSERT_EQ(memcmp(static_cast<void*>(&zeroed), - static_cast<void*>(&entry->ipv6), sizeof(zeroed)), - 0); - - // target_offset should be zero. - EXPECT_EQ(entry->target_offset, sizeof(ip6t_entry)); - - if (entry_offset < kEmptyStandardEntrySize * 4) { - // The first 4 entries are standard targets - struct xt_standard_target* target = - reinterpret_cast<struct xt_standard_target*>(entry->elems); - EXPECT_EQ(entry->next_offset, kEmptyStandardEntrySize); - EXPECT_EQ(target->target.u.user.target_size, sizeof(*target)); - EXPECT_EQ(strcmp(target->target.u.user.name, ""), 0); - EXPECT_EQ(target->target.u.user.revision, 0); - // This is what's returned for an accept verdict. I don't know why. - EXPECT_EQ(target->verdict, -NF_ACCEPT - 1); - } else { - // The last entry is an error target - struct xt_error_target* target = - reinterpret_cast<struct xt_error_target*>(entry->elems); - EXPECT_EQ(entry->next_offset, kEmptyErrorEntrySize); - EXPECT_EQ(target->target.u.user.target_size, sizeof(*target)); - EXPECT_EQ(strcmp(target->target.u.user.name, kErrorTarget), 0); - EXPECT_EQ(target->target.u.user.revision, 0); - EXPECT_EQ(strcmp(target->errorname, kErrorTarget), 0); - } - - entry_offset += entry->next_offset; - break; - } - - free(entries); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc deleted file mode 100644 index 98d07ae85..000000000 --- a/test/syscalls/linux/ip_socket_test_util.cc +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/ip_socket_test_util.h" - -#include <net/if.h> -#include <netinet/in.h> -#include <sys/socket.h> - -#include <cstring> - -namespace gvisor { -namespace testing { - -uint32_t IPFromInetSockaddr(const struct sockaddr* addr) { - auto* in_addr = reinterpret_cast<const struct sockaddr_in*>(addr); - return in_addr->sin_addr.s_addr; -} - -uint16_t PortFromInetSockaddr(const struct sockaddr* addr) { - auto* in_addr = reinterpret_cast<const struct sockaddr_in*>(addr); - return ntohs(in_addr->sin_port); -} - -PosixErrorOr<int> InterfaceIndex(std::string name) { - int index = if_nametoindex(name.c_str()); - if (index) { - return index; - } - return PosixError(errno); -} - -namespace { - -std::string DescribeSocketType(int type) { - return absl::StrCat(((type & SOCK_NONBLOCK) != 0) ? "non-blocking " : "", - ((type & SOCK_CLOEXEC) != 0) ? "close-on-exec " : ""); -} - -} // namespace - -SocketPairKind IPv6TCPAcceptBindSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected IPv6 TCP socket"); - return SocketPairKind{ - description, AF_INET6, type | SOCK_STREAM, IPPROTO_TCP, - TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM, 0, - /* dual_stack = */ false)}; -} - -SocketPairKind IPv4TCPAcceptBindSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected IPv4 TCP socket"); - return SocketPairKind{ - description, AF_INET, type | SOCK_STREAM, IPPROTO_TCP, - TCPAcceptBindSocketPairCreator(AF_INET, type | SOCK_STREAM, 0, - /* dual_stack = */ false)}; -} - -SocketPairKind DualStackTCPAcceptBindSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected dual stack TCP socket"); - return SocketPairKind{ - description, AF_INET6, type | SOCK_STREAM, IPPROTO_TCP, - TCPAcceptBindSocketPairCreator(AF_INET6, type | SOCK_STREAM, 0, - /* dual_stack = */ true)}; -} - -SocketPairKind IPv6TCPAcceptBindPersistentListenerSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected IPv6 TCP socket"); - return SocketPairKind{description, AF_INET6, type | SOCK_STREAM, IPPROTO_TCP, - TCPAcceptBindPersistentListenerSocketPairCreator( - AF_INET6, type | SOCK_STREAM, 0, - /* dual_stack = */ false)}; -} - -SocketPairKind IPv4TCPAcceptBindPersistentListenerSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected IPv4 TCP socket"); - return SocketPairKind{description, AF_INET, type | SOCK_STREAM, IPPROTO_TCP, - TCPAcceptBindPersistentListenerSocketPairCreator( - AF_INET, type | SOCK_STREAM, 0, - /* dual_stack = */ false)}; -} - -SocketPairKind DualStackTCPAcceptBindPersistentListenerSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected dual stack TCP socket"); - return SocketPairKind{description, AF_INET6, type | SOCK_STREAM, IPPROTO_TCP, - TCPAcceptBindPersistentListenerSocketPairCreator( - AF_INET6, type | SOCK_STREAM, 0, - /* dual_stack = */ true)}; -} - -SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected IPv6 UDP socket"); - return SocketPairKind{ - description, AF_INET6, type | SOCK_DGRAM, IPPROTO_UDP, - UDPBidirectionalBindSocketPairCreator(AF_INET6, type | SOCK_DGRAM, 0, - /* dual_stack = */ false)}; -} - -SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected IPv4 UDP socket"); - return SocketPairKind{ - description, AF_INET, type | SOCK_DGRAM, IPPROTO_UDP, - UDPBidirectionalBindSocketPairCreator(AF_INET, type | SOCK_DGRAM, 0, - /* dual_stack = */ false)}; -} - -SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "connected dual stack UDP socket"); - return SocketPairKind{ - description, AF_INET6, type | SOCK_DGRAM, IPPROTO_UDP, - UDPBidirectionalBindSocketPairCreator(AF_INET6, type | SOCK_DGRAM, 0, - /* dual_stack = */ true)}; -} - -SocketPairKind IPv4UDPUnboundSocketPair(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); - return SocketPairKind{ - description, AF_INET, type | SOCK_DGRAM, IPPROTO_UDP, - UDPUnboundSocketPairCreator(AF_INET, type | SOCK_DGRAM, 0, - /* dual_stack = */ false)}; -} - -SocketKind IPv4UDPUnboundSocket(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket"); - return SocketKind{ - description, AF_INET, type | SOCK_DGRAM, IPPROTO_UDP, - UnboundSocketCreator(AF_INET, type | SOCK_DGRAM, IPPROTO_UDP)}; -} - -SocketKind IPv6UDPUnboundSocket(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "IPv6 UDP socket"); - return SocketKind{ - description, AF_INET6, type | SOCK_DGRAM, IPPROTO_UDP, - UnboundSocketCreator(AF_INET6, type | SOCK_DGRAM, IPPROTO_UDP)}; -} - -SocketKind IPv4TCPUnboundSocket(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "IPv4 TCP socket"); - return SocketKind{ - description, AF_INET, type | SOCK_STREAM, IPPROTO_TCP, - UnboundSocketCreator(AF_INET, type | SOCK_STREAM, IPPROTO_TCP)}; -} - -SocketKind IPv6TCPUnboundSocket(int type) { - std::string description = - absl::StrCat(DescribeSocketType(type), "IPv6 TCP socket"); - return SocketKind{ - description, AF_INET6, type | SOCK_STREAM, IPPROTO_TCP, - UnboundSocketCreator(AF_INET6, type | SOCK_STREAM, IPPROTO_TCP)}; -} - -PosixError IfAddrHelper::Load() { - Release(); - RETURN_ERROR_IF_SYSCALL_FAIL(getifaddrs(&ifaddr_)); - return NoError(); -} - -void IfAddrHelper::Release() { - if (ifaddr_) { - freeifaddrs(ifaddr_); - ifaddr_ = nullptr; - } -} - -std::vector<std::string> IfAddrHelper::InterfaceList(int family) const { - std::vector<std::string> names; - for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) { - continue; - } - names.emplace(names.end(), ifa->ifa_name); - } - return names; -} - -const sockaddr* IfAddrHelper::GetAddr(int family, std::string name) const { - for (auto ifa = ifaddr_; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != family) { - continue; - } - if (name == ifa->ifa_name) { - return ifa->ifa_addr; - } - } - return nullptr; -} - -PosixErrorOr<int> IfAddrHelper::GetIndex(std::string name) const { - return InterfaceIndex(name); -} - -std::string GetAddr4Str(const in_addr* a) { - char str[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, a, str, sizeof(str)); - return std::string(str); -} - -std::string GetAddr6Str(const in6_addr* a) { - char str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET6, a, str, sizeof(str)); - return std::string(str); -} - -std::string GetAddrStr(const sockaddr* a) { - if (a->sa_family == AF_INET) { - auto src = &(reinterpret_cast<const sockaddr_in*>(a)->sin_addr); - return GetAddr4Str(src); - } else if (a->sa_family == AF_INET6) { - auto src = &(reinterpret_cast<const sockaddr_in6*>(a)->sin6_addr); - return GetAddr6Str(src); - } - return std::string("<invalid>"); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h deleted file mode 100644 index 9c3859fcd..000000000 --- a/test/syscalls/linux/ip_socket_test_util.h +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_ - -#include <arpa/inet.h> -#include <ifaddrs.h> -#include <sys/types.h> - -#include <string> - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Extracts the IP address from an inet sockaddr in network byte order. -uint32_t IPFromInetSockaddr(const struct sockaddr* addr); - -// Extracts the port from an inet sockaddr in host byte order. -uint16_t PortFromInetSockaddr(const struct sockaddr* addr); - -// InterfaceIndex returns the index of the named interface. -PosixErrorOr<int> InterfaceIndex(std::string name); - -// IPv6TCPAcceptBindSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and accept() syscalls with AF_INET6 and the -// given type bound to the IPv6 loopback. -SocketPairKind IPv6TCPAcceptBindSocketPair(int type); - -// IPv4TCPAcceptBindSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and accept() syscalls with AF_INET and the -// given type bound to the IPv4 loopback. -SocketPairKind IPv4TCPAcceptBindSocketPair(int type); - -// DualStackTCPAcceptBindSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and accept() syscalls with AF_INET6 and the -// given type bound to the IPv4 loopback. -SocketPairKind DualStackTCPAcceptBindSocketPair(int type); - -// IPv6TCPAcceptBindPersistentListenerSocketPair is like -// IPv6TCPAcceptBindSocketPair except it uses a persistent listening socket to -// create all socket pairs. -SocketPairKind IPv6TCPAcceptBindPersistentListenerSocketPair(int type); - -// IPv4TCPAcceptBindPersistentListenerSocketPair is like -// IPv4TCPAcceptBindSocketPair except it uses a persistent listening socket to -// create all socket pairs. -SocketPairKind IPv4TCPAcceptBindPersistentListenerSocketPair(int type); - -// DualStackTCPAcceptBindPersistentListenerSocketPair is like -// DualStackTCPAcceptBindSocketPair except it uses a persistent listening socket -// to create all socket pairs. -SocketPairKind DualStackTCPAcceptBindPersistentListenerSocketPair(int type); - -// IPv6UDPBidirectionalBindSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and connect() syscalls with AF_INET6 and the -// given type bound to the IPv6 loopback. -SocketPairKind IPv6UDPBidirectionalBindSocketPair(int type); - -// IPv4UDPBidirectionalBindSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and connect() syscalls with AF_INET and the -// given type bound to the IPv4 loopback. -SocketPairKind IPv4UDPBidirectionalBindSocketPair(int type); - -// DualStackUDPBidirectionalBindSocketPair returns a SocketPairKind that -// represents SocketPairs created with bind() and connect() syscalls with -// AF_INET6 and the given type bound to the IPv4 loopback. -SocketPairKind DualStackUDPBidirectionalBindSocketPair(int type); - -// IPv4UDPUnboundSocketPair returns a SocketPairKind that represents -// SocketPairs created with AF_INET and the given type. -SocketPairKind IPv4UDPUnboundSocketPair(int type); - -// IPv4UDPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET, SOCK_DGRAM, and the given type. -SocketKind IPv4UDPUnboundSocket(int type); - -// IPv6UDPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET6, SOCK_DGRAM, and the given type. -SocketKind IPv6UDPUnboundSocket(int type); - -// IPv4TCPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET, SOCK_STREAM and the given type. -SocketKind IPv4TCPUnboundSocket(int type); - -// IPv6TCPUnboundSocket returns a SocketKind that represents a SimpleSocket -// created with AF_INET6, SOCK_STREAM and the given type. -SocketKind IPv6TCPUnboundSocket(int type); - -// IfAddrHelper is a helper class that determines the local interfaces present -// and provides functions to obtain their names, index numbers, and IP address. -class IfAddrHelper { - public: - IfAddrHelper() : ifaddr_(nullptr) {} - ~IfAddrHelper() { Release(); } - - PosixError Load(); - void Release(); - - std::vector<std::string> InterfaceList(int family) const; - - const sockaddr* GetAddr(int family, std::string name) const; - PosixErrorOr<int> GetIndex(std::string name) const; - - private: - struct ifaddrs* ifaddr_; -}; - -// GetAddr4Str returns the given IPv4 network address structure as a string. -std::string GetAddr4Str(const in_addr* a); - -// GetAddr6Str returns the given IPv6 network address structure as a string. -std::string GetAddr6Str(const in6_addr* a); - -// GetAddrStr returns the given IPv4 or IPv6 network address structure as a -// string. -std::string GetAddrStr(const sockaddr* a); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_IP_SOCKET_TEST_UTIL_H_ diff --git a/test/syscalls/linux/iptables.cc b/test/syscalls/linux/iptables.cc deleted file mode 100644 index 22550b800..000000000 --- a/test/syscalls/linux/iptables.cc +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2019 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/syscalls/linux/iptables.h" - -#include <arpa/inet.h> -#include <linux/capability.h> -#include <linux/netfilter/x_tables.h> -#include <net/if.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <stdio.h> -#include <sys/poll.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> - -#include "gtest/gtest.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr char kNatTablename[] = "nat"; -constexpr char kErrorTarget[] = "ERROR"; -constexpr size_t kEmptyStandardEntrySize = - sizeof(struct ipt_entry) + sizeof(struct ipt_standard_target); -constexpr size_t kEmptyErrorEntrySize = - sizeof(struct ipt_entry) + sizeof(struct ipt_error_target); - -TEST(IPTablesBasic, CreateSocket) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), - SyscallSucceeds()); - - ASSERT_THAT(close(sock), SyscallSucceeds()); -} - -TEST(IPTablesBasic, FailSockoptNonRaw) { - // Even if the user has CAP_NET_RAW, they shouldn't be able to use the - // iptables sockopts with a non-raw socket. - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds()); - - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info); - EXPECT_THAT(getsockopt(sock, SOL_IP, IPT_SO_GET_INFO, &info, &info_size), - SyscallFailsWithErrno(ENOPROTOOPT)); - - ASSERT_THAT(close(sock), SyscallSucceeds()); -} - -TEST(IPTablesBasic, GetInfoErrorPrecedence) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds()); - - // When using the wrong type of socket and a too-short optlen, we should get - // EINVAL. - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info) - 1; - ASSERT_THAT(getsockopt(sock, SOL_IP, IPT_SO_GET_INFO, &info, &info_size), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(IPTablesBasic, GetEntriesErrorPrecedence) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET, SOCK_DGRAM, 0), SyscallSucceeds()); - - // When using the wrong type of socket and a too-short optlen, we should get - // EINVAL. - struct ipt_get_entries entries = {}; - socklen_t entries_size = sizeof(struct ipt_get_entries) - 1; - snprintf(entries.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - ASSERT_THAT( - getsockopt(sock, SOL_IP, IPT_SO_GET_ENTRIES, &entries, &entries_size), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(IPTablesBasic, OriginalDstErrors) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET, SOCK_STREAM, 0), SyscallSucceeds()); - - // Sockets not affected by NAT should fail to find an original destination. - struct sockaddr_in addr = {}; - socklen_t addr_len = sizeof(addr); - EXPECT_THAT(getsockopt(sock, SOL_IP, SO_ORIGINAL_DST, &addr, &addr_len), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST(IPTablesBasic, GetRevision) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), - SyscallSucceeds()); - - struct xt_get_revision rev = {}; - socklen_t rev_len = sizeof(rev); - - snprintf(rev.name, sizeof(rev.name), "REDIRECT"); - rev.revision = 0; - - // Revision 0 exists. - EXPECT_THAT( - getsockopt(sock, SOL_IP, IPT_SO_GET_REVISION_TARGET, &rev, &rev_len), - SyscallSucceeds()); - EXPECT_EQ(rev.revision, 0); - - // Revisions > 0 don't exist. - rev.revision = 1; - EXPECT_THAT( - getsockopt(sock, SOL_IP, IPT_SO_GET_REVISION_TARGET, &rev, &rev_len), - SyscallFailsWithErrno(EPROTONOSUPPORT)); -} - -// Fixture for iptables tests. -class IPTablesTest : public ::testing::Test { - protected: - // Creates a socket to be used in tests. - void SetUp() override; - - // Closes the socket created by SetUp(). - void TearDown() override; - - // The socket via which to manipulate iptables. - int s_; -}; - -void IPTablesTest::SetUp() { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT(s_ = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), SyscallSucceeds()); -} - -void IPTablesTest::TearDown() { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - EXPECT_THAT(close(s_), SyscallSucceeds()); -} - -// This tests the initial state of a machine with empty iptables. We don't have -// a guarantee that the iptables are empty when running in native, but we can -// test that gVisor has the same initial state that a newly-booted Linux machine -// would have. -TEST_F(IPTablesTest, InitialState) { - SKIP_IF(!IsRunningOnGvisor()); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // - // Get info via sockopt. - // - struct ipt_getinfo info = {}; - snprintf(info.name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - socklen_t info_size = sizeof(info); - ASSERT_THAT(getsockopt(s_, SOL_IP, IPT_SO_GET_INFO, &info, &info_size), - SyscallSucceeds()); - - // The nat table supports PREROUTING, and OUTPUT. - unsigned int valid_hooks = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT) | - (1 << NF_IP_POST_ROUTING) | (1 << NF_IP_LOCAL_IN); - - EXPECT_EQ(info.valid_hooks, valid_hooks); - - // Each chain consists of an empty entry with a standard target.. - EXPECT_EQ(info.hook_entry[NF_IP_PRE_ROUTING], 0); - EXPECT_EQ(info.hook_entry[NF_IP_LOCAL_IN], kEmptyStandardEntrySize); - EXPECT_EQ(info.hook_entry[NF_IP_LOCAL_OUT], kEmptyStandardEntrySize * 2); - EXPECT_EQ(info.hook_entry[NF_IP_POST_ROUTING], kEmptyStandardEntrySize * 3); - - // The underflow points are the same as the entry points. - EXPECT_EQ(info.underflow[NF_IP_PRE_ROUTING], 0); - EXPECT_EQ(info.underflow[NF_IP_LOCAL_IN], kEmptyStandardEntrySize); - EXPECT_EQ(info.underflow[NF_IP_LOCAL_OUT], kEmptyStandardEntrySize * 2); - EXPECT_EQ(info.underflow[NF_IP_POST_ROUTING], kEmptyStandardEntrySize * 3); - - // One entry for each chain, plus an error entry at the end. - EXPECT_EQ(info.num_entries, 5); - - EXPECT_EQ(info.size, 4 * kEmptyStandardEntrySize + kEmptyErrorEntrySize); - EXPECT_EQ(strcmp(info.name, kNatTablename), 0); - - // - // Use info to get entries. - // - socklen_t entries_size = sizeof(struct ipt_get_entries) + info.size; - struct ipt_get_entries* entries = - static_cast<struct ipt_get_entries*>(malloc(entries_size)); - snprintf(entries->name, XT_TABLE_MAXNAMELEN, "%s", kNatTablename); - entries->size = info.size; - ASSERT_THAT( - getsockopt(s_, SOL_IP, IPT_SO_GET_ENTRIES, entries, &entries_size), - SyscallSucceeds()); - - // Verify the name and size. - ASSERT_EQ(info.size, entries->size); - ASSERT_EQ(strcmp(entries->name, kNatTablename), 0); - - // Verify that the entrytable is 4 entries with accept targets and no matches - // followed by a single error target. - size_t entry_offset = 0; - while (entry_offset < entries->size) { - struct ipt_entry* entry = reinterpret_cast<struct ipt_entry*>( - reinterpret_cast<char*>(entries->entrytable) + entry_offset); - - // ip should be zeroes. - struct ipt_ip zeroed = {}; - EXPECT_EQ(memcmp(static_cast<void*>(&zeroed), - static_cast<void*>(&entry->ip), sizeof(zeroed)), - 0); - - // target_offset should be zero. - EXPECT_EQ(entry->target_offset, sizeof(ipt_entry)); - - if (entry_offset < kEmptyStandardEntrySize * 4) { - // The first 4 entries are standard targets - struct ipt_standard_target* target = - reinterpret_cast<struct ipt_standard_target*>(entry->elems); - EXPECT_EQ(entry->next_offset, kEmptyStandardEntrySize); - EXPECT_EQ(target->target.u.user.target_size, sizeof(*target)); - EXPECT_EQ(strcmp(target->target.u.user.name, ""), 0); - EXPECT_EQ(target->target.u.user.revision, 0); - // This is what's returned for an accept verdict. I don't know why. - EXPECT_EQ(target->verdict, -NF_ACCEPT - 1); - } else { - // The last entry is an error target - struct ipt_error_target* target = - reinterpret_cast<struct ipt_error_target*>(entry->elems); - EXPECT_EQ(entry->next_offset, kEmptyErrorEntrySize); - EXPECT_EQ(target->target.u.user.target_size, sizeof(*target)); - EXPECT_EQ(strcmp(target->target.u.user.name, kErrorTarget), 0); - EXPECT_EQ(target->target.u.user.revision, 0); - EXPECT_EQ(strcmp(target->errorname, kErrorTarget), 0); - } - - entry_offset += entry->next_offset; - } - - free(entries); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/iptables.h b/test/syscalls/linux/iptables.h deleted file mode 100644 index d0fc10fea..000000000 --- a/test/syscalls/linux/iptables.h +++ /dev/null @@ -1,302 +0,0 @@ -// Copyright 2019 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. - -// There are a number of structs and values that we can't #include because of a -// difference between C and C++ (C++ won't let you implicitly cast from void* to -// struct something*). We re-define them here. - -#ifndef GVISOR_TEST_SYSCALLS_IPTABLES_TYPES_H_ -#define GVISOR_TEST_SYSCALLS_IPTABLES_TYPES_H_ - -// Netfilter headers require some headers to preceed them. -// clang-format off -#include <netinet/in.h> -#include <stddef.h> -// clang-format on - -#include <linux/netfilter/x_tables.h> -#include <linux/netfilter_ipv4.h> -#include <linux/netfilter_ipv6.h> -#include <net/if.h> -#include <netinet/ip.h> -#include <stdint.h> - -// -// IPv4 ABI. -// - -#define ipt_standard_target xt_standard_target -#define ipt_entry_target xt_entry_target -#define ipt_error_target xt_error_target - -enum SockOpts { - // For setsockopt. - IPT_BASE_CTL = 64, - IPT_SO_SET_REPLACE = IPT_BASE_CTL, - IPT_SO_SET_ADD_COUNTERS = IPT_BASE_CTL + 1, - IPT_SO_SET_MAX = IPT_SO_SET_ADD_COUNTERS, - - // For getsockopt. - IPT_SO_GET_INFO = IPT_BASE_CTL, - IPT_SO_GET_ENTRIES = IPT_BASE_CTL + 1, - IPT_SO_GET_REVISION_MATCH = IPT_BASE_CTL + 2, - IPT_SO_GET_REVISION_TARGET = IPT_BASE_CTL + 3, - IPT_SO_GET_MAX = IPT_SO_GET_REVISION_TARGET -}; - -// ipt_ip specifies basic matching criteria that can be applied by examining -// only the IP header of a packet. -struct ipt_ip { - // Source IP address. - struct in_addr src; - - // Destination IP address. - struct in_addr dst; - - // Source IP address mask. - struct in_addr smsk; - - // Destination IP address mask. - struct in_addr dmsk; - - // Input interface. - char iniface[IFNAMSIZ]; - - // Output interface. - char outiface[IFNAMSIZ]; - - // Input interface mask. - unsigned char iniface_mask[IFNAMSIZ]; - - // Output interface mask. - unsigned char outiface_mask[IFNAMSIZ]; - - // Transport protocol. - uint16_t proto; - - // Flags. - uint8_t flags; - - // Inverse flags. - uint8_t invflags; -}; - -// ipt_entry is an iptables rule. It contains information about what packets the -// rule matches and what action (target) to perform for matching packets. -struct ipt_entry { - // Basic matching information used to match a packet's IP header. - struct ipt_ip ip; - - // A caching field that isn't used by userspace. - unsigned int nfcache; - - // The number of bytes between the start of this ipt_entry struct and the - // rule's target. - uint16_t target_offset; - - // The total size of this rule, from the beginning of the entry to the end of - // the target. - uint16_t next_offset; - - // A return pointer not used by userspace. - unsigned int comefrom; - - // Counters for packets and bytes, which we don't yet implement. - struct xt_counters counters; - - // The data for all this rules matches followed by the target. This runs - // beyond the value of sizeof(struct ipt_entry). - unsigned char elems[0]; -}; - -// Passed to getsockopt(IPT_SO_GET_INFO). -struct ipt_getinfo { - // The name of the table. The user only fills this in, the rest is filled in - // when returning from getsockopt. Currently "nat" and "mangle" are supported. - char name[XT_TABLE_MAXNAMELEN]; - - // A bitmap of which hooks apply to the table. For example, a table with hooks - // PREROUTING and FORWARD has the value - // (1 << NF_IP_PRE_REOUTING) | (1 << NF_IP_FORWARD). - unsigned int valid_hooks; - - // The offset into the entry table for each valid hook. The entry table is - // returned by getsockopt(IPT_SO_GET_ENTRIES). - unsigned int hook_entry[NF_IP_NUMHOOKS]; - - // For each valid hook, the underflow is the offset into the entry table to - // jump to in case traversing the table yields no verdict (although I have no - // clue how that could happen - builtin chains always end with a policy, and - // user-defined chains always end with a RETURN. - // - // The entry referred to must be an "unconditional" entry, meaning it has no - // matches, specifies no IP criteria, and either DROPs or ACCEPTs packets. It - // basically has to be capable of making a definitive decision no matter what - // it's passed. - unsigned int underflow[NF_IP_NUMHOOKS]; - - // The number of entries in the entry table returned by - // getsockopt(IPT_SO_GET_ENTRIES). - unsigned int num_entries; - - // The size of the entry table returned by getsockopt(IPT_SO_GET_ENTRIES). - unsigned int size; -}; - -// Passed to getsockopt(IPT_SO_GET_ENTRIES). -struct ipt_get_entries { - // The name of the table. The user fills this in. Currently "nat" and "mangle" - // are supported. - char name[XT_TABLE_MAXNAMELEN]; - - // The size of the entry table in bytes. The user fills this in with the value - // from struct ipt_getinfo.size. - unsigned int size; - - // The entries for the given table. This will run past the size defined by - // sizeof(struct ipt_get_entries). - struct ipt_entry entrytable[0]; -}; - -// Passed to setsockopt(SO_SET_REPLACE). -struct ipt_replace { - // The name of the table. - char name[XT_TABLE_MAXNAMELEN]; - - // The same as struct ipt_getinfo.valid_hooks. Users don't change this. - unsigned int valid_hooks; - - // The same as struct ipt_getinfo.num_entries. - unsigned int num_entries; - - // The same as struct ipt_getinfo.size. - unsigned int size; - - // The same as struct ipt_getinfo.hook_entry. - unsigned int hook_entry[NF_IP_NUMHOOKS]; - - // The same as struct ipt_getinfo.underflow. - unsigned int underflow[NF_IP_NUMHOOKS]; - - // The number of counters, which should equal the number of entries. - unsigned int num_counters; - - // The unchanged values from each ipt_entry's counters. - struct xt_counters* counters; - - // The entries to write to the table. This will run past the size defined by - // sizeof(srtuct ipt_replace); - struct ipt_entry entries[0]; -}; - -// -// IPv6 ABI. -// - -enum SockOpts6 { - // For setsockopt. - IP6T_BASE_CTL = 64, - IP6T_SO_SET_REPLACE = IP6T_BASE_CTL, - IP6T_SO_SET_ADD_COUNTERS = IP6T_BASE_CTL + 1, - IP6T_SO_SET_MAX = IP6T_SO_SET_ADD_COUNTERS, - - // For getsockopt. - IP6T_SO_GET_INFO = IP6T_BASE_CTL, - IP6T_SO_GET_ENTRIES = IP6T_BASE_CTL + 1, - IP6T_SO_GET_REVISION_MATCH = IP6T_BASE_CTL + 4, - IP6T_SO_GET_REVISION_TARGET = IP6T_BASE_CTL + 5, - IP6T_SO_GET_MAX = IP6T_SO_GET_REVISION_TARGET -}; - -// ip6t_ip6 specifies basic matching criteria that can be applied by examining -// only the IP header of a packet. -struct ip6t_ip6 { - // Source IP address. - struct in6_addr src; - - // Destination IP address. - struct in6_addr dst; - - // Source IP address mask. - struct in6_addr smsk; - - // Destination IP address mask. - struct in6_addr dmsk; - - // Input interface. - char iniface[IFNAMSIZ]; - - // Output interface. - char outiface[IFNAMSIZ]; - - // Input interface mask. - unsigned char iniface_mask[IFNAMSIZ]; - - // Output interface mask. - unsigned char outiface_mask[IFNAMSIZ]; - - // Transport protocol. - uint16_t proto; - - // TOS. - uint8_t tos; - - // Flags. - uint8_t flags; - - // Inverse flags. - uint8_t invflags; -}; - -// ip6t_entry is an ip6tables rule. -struct ip6t_entry { - // Basic matching information used to match a packet's IP header. - struct ip6t_ip6 ipv6; - - // A caching field that isn't used by userspace. - unsigned int nfcache; - - // The number of bytes between the start of this entry and the rule's target. - uint16_t target_offset; - - // The total size of this rule, from the beginning of the entry to the end of - // the target. - uint16_t next_offset; - - // A return pointer not used by userspace. - unsigned int comefrom; - - // Counters for packets and bytes, which we don't yet implement. - struct xt_counters counters; - - // The data for all this rules matches followed by the target. This runs - // beyond the value of sizeof(struct ip6t_entry). - unsigned char elems[0]; -}; - -// Passed to getsockopt(IP6T_SO_GET_ENTRIES). -struct ip6t_get_entries { - // The name of the table. - char name[XT_TABLE_MAXNAMELEN]; - - // The size of the entry table in bytes. The user fills this in with the value - // from struct ipt_getinfo.size. - unsigned int size; - - // The entries for the given table. This will run past the size defined by - // sizeof(struct ip6t_get_entries). - struct ip6t_entry entrytable[0]; -}; - -#endif // GVISOR_TEST_SYSCALLS_IPTABLES_TYPES_H_ diff --git a/test/syscalls/linux/itimer.cc b/test/syscalls/linux/itimer.cc deleted file mode 100644 index e397d5f57..000000000 --- a/test/syscalls/linux/itimer.cc +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <sys/types.h> -#include <time.h> - -#include <atomic> -#include <functional> -#include <iostream> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.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/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { -namespace { - -constexpr char kSIGALRMToMainThread[] = "--itimer_sigarlm_to_main_thread"; -constexpr char kSIGPROFFairnessActive[] = "--itimer_sigprof_fairness_active"; -constexpr char kSIGPROFFairnessIdle[] = "--itimer_sigprof_fairness_idle"; - -// Time period to be set for the itimers. -constexpr absl::Duration kPeriod = absl::Milliseconds(25); -// Total amount of time to spend per thread. -constexpr absl::Duration kTestDuration = absl::Seconds(20); -// Amount of spin iterations to perform as the minimum work item per thread. -// Chosen to be sub-millisecond range. -constexpr int kIterations = 10000000; -// Allow deviation in the number of samples. -constexpr double kNumSamplesDeviationRatio = 0.2; - -TEST(ItimerTest, ItimervalUpdatedBeforeExpiration) { - constexpr int kSleepSecs = 10; - constexpr int kAlarmSecs = 15; - static_assert( - kSleepSecs < kAlarmSecs, - "kSleepSecs must be less than kAlarmSecs for the test to be meaningful"); - constexpr int kMaxRemainingSecs = kAlarmSecs - kSleepSecs; - - // Install a no-op handler for SIGALRM. - struct sigaction sa = {}; - sigfillset(&sa.sa_mask); - sa.sa_handler = +[](int signo) {}; - auto const cleanup_sa = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); - - // Set an itimer-based alarm for kAlarmSecs from now. - struct itimerval itv = {}; - itv.it_value.tv_sec = kAlarmSecs; - auto const cleanup_itimer = - ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv)); - - // After sleeping for kSleepSecs, the itimer value should reflect the elapsed - // time even if it hasn't expired. - absl::SleepFor(absl::Seconds(kSleepSecs)); - ASSERT_THAT(getitimer(ITIMER_REAL, &itv), SyscallSucceeds()); - EXPECT_TRUE( - itv.it_value.tv_sec < kMaxRemainingSecs || - (itv.it_value.tv_sec == kMaxRemainingSecs && itv.it_value.tv_usec == 0)) - << "Remaining time: " << itv.it_value.tv_sec << " seconds + " - << itv.it_value.tv_usec << " microseconds"; -} - -ABSL_CONST_INIT static thread_local std::atomic_int signal_test_num_samples = - ATOMIC_VAR_INIT(0); - -void SignalTestSignalHandler(int /*signum*/) { signal_test_num_samples++; } - -struct SignalTestResult { - int expected_total; - int main_thread_samples; - std::vector<int> worker_samples; -}; - -std::ostream& operator<<(std::ostream& os, const SignalTestResult& r) { - os << "{expected_total: " << r.expected_total - << ", main_thread_samples: " << r.main_thread_samples - << ", worker_samples: ["; - bool first = true; - for (int sample : r.worker_samples) { - if (!first) { - os << ", "; - } - os << sample; - first = false; - } - os << "]}"; - return os; -} - -// Starts two worker threads and itimer id and measures the number of signal -// delivered to each thread. -SignalTestResult ItimerSignalTest(int id, clock_t main_clock, - clock_t worker_clock, int signal, - absl::Duration sleep) { - signal_test_num_samples = 0; - - struct sigaction sa = {}; - sa.sa_handler = &SignalTestSignalHandler; - sa.sa_flags = SA_RESTART; - sigemptyset(&sa.sa_mask); - auto sigaction_cleanup = ScopedSigaction(signal, sa).ValueOrDie(); - - int socketfds[2]; - TEST_PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, socketfds) == 0); - - // Do the spinning in the workers. - std::function<void*(int)> work = [&](int socket_fd) { - FileDescriptor fd(socket_fd); - - absl::Time finish = Now(worker_clock) + kTestDuration; - while (Now(worker_clock) < finish) { - // Blocked on read. - char c; - RetryEINTR(read)(fd.get(), &c, 1); - for (int i = 0; i < kIterations; i++) { - // Ensure compiler won't optimize this loop away. - asm(""); - } - - if (sleep != absl::ZeroDuration()) { - // Sleep so that the entire process is idle for a while. - absl::SleepFor(sleep); - } - - // Unblock the other thread. - RetryEINTR(write)(fd.get(), &c, 1); - } - - return reinterpret_cast<void*>(signal_test_num_samples.load()); - }; - - ScopedThread th1( - static_cast<std::function<void*()>>(std::bind(work, socketfds[0]))); - ScopedThread th2( - static_cast<std::function<void*()>>(std::bind(work, socketfds[1]))); - - absl::Time start = Now(main_clock); - // Start the timer. - struct itimerval timer = {}; - timer.it_value = absl::ToTimeval(kPeriod); - timer.it_interval = absl::ToTimeval(kPeriod); - auto cleanup_itimer = ScopedItimer(id, timer).ValueOrDie(); - - // Unblock th1. - // - // N.B. th2 owns socketfds[1] but can't close it until it unblocks. - char c = 0; - TEST_CHECK(write(socketfds[1], &c, 1) == 1); - - SignalTestResult result; - - // Wait for the workers to be done and collect their sample counts. - result.worker_samples.push_back(reinterpret_cast<int64_t>(th1.Join())); - result.worker_samples.push_back(reinterpret_cast<int64_t>(th2.Join())); - cleanup_itimer.Release()(); - result.expected_total = (Now(main_clock) - start) / kPeriod; - result.main_thread_samples = signal_test_num_samples.load(); - - return result; -} - -int TestSIGALRMToMainThread() { - SignalTestResult result = - ItimerSignalTest(ITIMER_REAL, CLOCK_REALTIME, CLOCK_REALTIME, SIGALRM, - absl::ZeroDuration()); - - std::cerr << "result: " << result << std::endl; - - // ITIMER_REAL-generated SIGALRMs prefer to deliver to the thread group leader - // (but don't guarantee it), so we expect to see most samples on the main - // thread. - // - // The number of SIGALRMs delivered to a worker should not exceed 20% - // of the number of total signals expected (this is somewhat arbitrary). - const int worker_threshold = result.expected_total / 5; - - // - // Linux only guarantees timers will never expire before the requested time. - // Thus, we only check the upper bound and also it at least have one sample. - TEST_CHECK(result.main_thread_samples <= result.expected_total); - TEST_CHECK(result.main_thread_samples > 0); - for (int num : result.worker_samples) { - TEST_CHECK_MSG(num <= worker_threshold, "worker received too many samples"); - } - - return 0; -} - -// Random save/restore is disabled as it introduces additional latency and -// unpredictable distribution patterns. -TEST(ItimerTest, DeliversSIGALRMToMainThread_NoRandomSave) { - pid_t child; - int execve_errno; - auto kill = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGALRMToMainThread}, - {}, &child, &execve_errno)); - EXPECT_EQ(0, execve_errno); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - - // Not required anymore. - kill.Release(); - - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; -} - -// Signals are delivered to threads fairly. -// -// sleep indicates how long to sleep worker threads each iteration to make the -// entire process idle. -int TestSIGPROFFairness(absl::Duration sleep) { - SignalTestResult result = - ItimerSignalTest(ITIMER_PROF, CLOCK_PROCESS_CPUTIME_ID, - CLOCK_THREAD_CPUTIME_ID, SIGPROF, sleep); - - std::cerr << "result: " << result << std::endl; - - // The number of samples on the main thread should be very low as it did - // nothing. - TEST_CHECK(result.main_thread_samples < 80); - - // Both workers should get roughly equal number of samples. - TEST_CHECK(result.worker_samples.size() == 2); - - TEST_CHECK(result.expected_total > 0); - - // In an ideal world each thread would get exactly 50% of the signals, - // but since that's unlikely to happen we allow for them to get no less than - // kNumSamplesDeviationRatio of the total observed samples. - TEST_CHECK_MSG(std::abs(result.worker_samples[0] - result.worker_samples[1]) < - ((result.worker_samples[0] + result.worker_samples[1]) * - kNumSamplesDeviationRatio), - "one worker received disproportionate share of samples"); - - return 0; -} - -// Random save/restore is disabled as it introduces additional latency and -// unpredictable distribution patterns. -TEST(ItimerTest, DeliversSIGPROFToThreadsRoughlyFairlyActive_NoRandomSave) { - // On the KVM and ptrace platforms, switches between sentry and application - // context are sometimes extremely slow, causing the itimer to send SIGPROF to - // a thread that either already has one pending or has had SIGPROF delivered, - // but hasn't handled it yet (and thus therefore still has SIGPROF masked). In - // either case, since itimer signals are group-directed, signal sending falls - // back to notifying the thread group leader. ItimerSignalTest() fails if "too - // many" signals are delivered to the thread group leader, so these tests are - // flaky on these platforms. - // - // TODO(b/143247272): Clarify why context switches are so slow on KVM. - const auto gvisor_platform = GvisorPlatform(); - SKIP_IF(gvisor_platform == Platform::kKVM || - gvisor_platform == Platform::kPtrace); - - pid_t child; - int execve_errno; - auto kill = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGPROFFairnessActive}, - {}, &child, &execve_errno)); - EXPECT_EQ(0, execve_errno); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - - // Not required anymore. - kill.Release(); - - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -// Random save/restore is disabled as it introduces additional latency and -// unpredictable distribution patterns. -TEST(ItimerTest, DeliversSIGPROFToThreadsRoughlyFairlyIdle_NoRandomSave) { - // See comment in DeliversSIGPROFToThreadsRoughlyFairlyActive. - const auto gvisor_platform = GvisorPlatform(); - SKIP_IF(gvisor_platform == Platform::kKVM || - gvisor_platform == Platform::kPtrace); - - pid_t child; - int execve_errno; - auto kill = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/proc/self/exe", {"/proc/self/exe", kSIGPROFFairnessIdle}, - {}, &child, &execve_errno)); - EXPECT_EQ(0, execve_errno); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - - // Not required anymore. - kill.Release(); - - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "Exited with code: " << status; -} - -} // namespace -} // namespace testing -} // namespace gvisor - -namespace { -void MaskSIGPIPE() { - // Always mask SIGPIPE as it's common and tests aren't expected to handle it. - // We don't take the TestInit() path so we must do this manually. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - TEST_CHECK(sigaction(SIGPIPE, &sa, nullptr) == 0); -} -} // namespace - -int main(int argc, char** argv) { - // These tests require no background threads, so check for them before - // TestInit. - for (int i = 0; i < argc; i++) { - absl::string_view arg(argv[i]); - - if (arg == gvisor::testing::kSIGALRMToMainThread) { - MaskSIGPIPE(); - return gvisor::testing::TestSIGALRMToMainThread(); - } - if (arg == gvisor::testing::kSIGPROFFairnessActive) { - MaskSIGPIPE(); - return gvisor::testing::TestSIGPROFFairness(absl::ZeroDuration()); - } - if (arg == gvisor::testing::kSIGPROFFairnessIdle) { - MaskSIGPIPE(); - // Sleep time > ClockTick (10ms) exercises sleeping gVisor's - // kernel.cpuClockTicker. - return gvisor::testing::TestSIGPROFFairness(absl::Milliseconds(25)); - } - } - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/kcov.cc b/test/syscalls/linux/kcov.cc deleted file mode 100644 index 6816c1fd0..000000000 --- a/test/syscalls/linux/kcov.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/errno.h> -#include <sys/ioctl.h> -#include <sys/mman.h> - -#include <atomic> - -#include "gtest/gtest.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// For this set of tests to run, they must be run with coverage enabled. On -// native Linux, this involves compiling the kernel with kcov enabled. For -// gVisor, we need to enable the Go coverage tool, e.g. bazel test -- -// collect_coverage_data --instrumentation_filter=//pkg/... <test>. - -constexpr char kcovPath[] = "/sys/kernel/debug/kcov"; -constexpr int kSize = 4096; -constexpr int KCOV_INIT_TRACE = 0x80086301; -constexpr int KCOV_ENABLE = 0x6364; -constexpr int KCOV_DISABLE = 0x6365; - -uint64_t* KcovMmap(int fd) { - return (uint64_t*)mmap(nullptr, kSize * sizeof(uint64_t), - PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); -} - -TEST(KcovTest, Kcov) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - int fd; - ASSERT_THAT(fd = open(kcovPath, O_RDWR), - AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT))); - // Kcov not available. - SKIP_IF(errno == ENOENT); - auto fd_closer = Cleanup([fd]() { close(fd); }); - - ASSERT_THAT(ioctl(fd, KCOV_INIT_TRACE, kSize), SyscallSucceeds()); - uint64_t* area = KcovMmap(fd); - ASSERT_TRUE(area != MAP_FAILED); - ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds()); - - for (int i = 0; i < 10; i++) { - // Make some syscalls to generate coverage data. - ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallFailsWithErrno(EINVAL)); - } - - uint64_t num_pcs = *(uint64_t*)(area); - EXPECT_GT(num_pcs, 0); - for (uint64_t i = 1; i <= num_pcs; i++) { - // Verify that PCs are in the standard kernel range. - EXPECT_GT(area[i], 0xffffffff7fffffffL); - } - - ASSERT_THAT(ioctl(fd, KCOV_DISABLE, 0), SyscallSucceeds()); -} - -TEST(KcovTest, PrematureMmap) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - int fd; - ASSERT_THAT(fd = open(kcovPath, O_RDWR), - AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT))); - // Kcov not available. - SKIP_IF(errno == ENOENT); - auto fd_closer = Cleanup([fd]() { close(fd); }); - - // Cannot mmap before KCOV_INIT_TRACE. - uint64_t* area = KcovMmap(fd); - ASSERT_TRUE(area == MAP_FAILED); -} - -// Tests that multiple kcov fds can be used simultaneously. -TEST(KcovTest, MultipleFds) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - int fd1; - ASSERT_THAT(fd1 = open(kcovPath, O_RDWR), - AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT))); - // Kcov not available. - SKIP_IF(errno == ENOENT); - - int fd2; - ASSERT_THAT(fd2 = open(kcovPath, O_RDWR), SyscallSucceeds()); - auto fd_closer = Cleanup([fd1, fd2]() { - close(fd1); - close(fd2); - }); - - auto t1 = ScopedThread([&] { - ASSERT_THAT(ioctl(fd1, KCOV_INIT_TRACE, kSize), SyscallSucceeds()); - uint64_t* area = KcovMmap(fd1); - ASSERT_TRUE(area != MAP_FAILED); - ASSERT_THAT(ioctl(fd1, KCOV_ENABLE, 0), SyscallSucceeds()); - }); - - ASSERT_THAT(ioctl(fd2, KCOV_INIT_TRACE, kSize), SyscallSucceeds()); - uint64_t* area = KcovMmap(fd2); - ASSERT_TRUE(area != MAP_FAILED); - ASSERT_THAT(ioctl(fd2, KCOV_ENABLE, 0), SyscallSucceeds()); -} - -// Tests behavior for two threads trying to use the same kcov fd. -TEST(KcovTest, MultipleThreads) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - int fd; - ASSERT_THAT(fd = open(kcovPath, O_RDWR), - AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT))); - // Kcov not available. - SKIP_IF(errno == ENOENT); - auto fd_closer = Cleanup([fd]() { close(fd); }); - - // Test the behavior of multiple threads trying to use the same kcov fd - // simultaneously. - std::atomic<bool> t1_enabled(false), t1_disabled(false), t2_failed(false), - t2_exited(false); - auto t1 = ScopedThread([&] { - ASSERT_THAT(ioctl(fd, KCOV_INIT_TRACE, kSize), SyscallSucceeds()); - uint64_t* area = KcovMmap(fd); - ASSERT_TRUE(area != MAP_FAILED); - ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds()); - t1_enabled = true; - - // After t2 has made sure that enabling kcov again fails, disable it. - while (!t2_failed) { - sched_yield(); - } - ASSERT_THAT(ioctl(fd, KCOV_DISABLE, 0), SyscallSucceeds()); - t1_disabled = true; - - // Wait for t2 to enable kcov and then exit, after which we should be able - // to enable kcov again, without needing to set up a new memory mapping. - while (!t2_exited) { - sched_yield(); - } - ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds()); - }); - - auto t2 = ScopedThread([&] { - // Wait for t1 to enable kcov, and make sure that enabling kcov again fails. - while (!t1_enabled) { - sched_yield(); - } - ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallFailsWithErrno(EINVAL)); - t2_failed = true; - - // Wait for t1 to disable kcov, after which using fd should now succeed. - while (!t1_disabled) { - sched_yield(); - } - uint64_t* area = KcovMmap(fd); - ASSERT_TRUE(area != MAP_FAILED); - ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds()); - }); - - t2.Join(); - t2_exited = true; -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/kill.cc b/test/syscalls/linux/kill.cc deleted file mode 100644 index 5d1735853..000000000 --- a/test/syscalls/linux/kill.cc +++ /dev/null @@ -1,389 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <cerrno> -#include <csignal> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.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" - -ABSL_FLAG(int32_t, scratch_uid, 65534, "scratch UID"); -ABSL_FLAG(int32_t, scratch_gid, 65534, "scratch GID"); - -using ::testing::Ge; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(KillTest, CanKillValidPid) { - // If pid is positive, then signal sig is sent to the process with the ID - // specified by pid. - EXPECT_THAT(kill(getpid(), 0), SyscallSucceeds()); - // If pid equals 0, then sig is sent to every process in the process group of - // the calling process. - EXPECT_THAT(kill(0, 0), SyscallSucceeds()); - - ScopedThread([] { EXPECT_THAT(kill(gettid(), 0), SyscallSucceeds()); }); -} - -void SigHandler(int sig, siginfo_t* info, void* context) { _exit(0); } - -// If pid equals -1, then sig is sent to every process for which the calling -// process has permission to send signals, except for process 1 (init). -TEST(KillTest, CanKillAllPIDs) { - // If we're not running inside the sandbox, then we skip this test - // as our namespace may contain may more processes that cannot tolerate - // the signal below. We also cannot reliably create a new pid namespace - // for ourselves and test the same functionality. - SKIP_IF(!IsRunningOnGvisor()); - - int pipe_fds[2]; - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - FileDescriptor read_fd(pipe_fds[0]); - FileDescriptor write_fd(pipe_fds[1]); - - pid_t pid = fork(); - if (pid == 0) { - read_fd.reset(); - - struct sigaction sa; - sa.sa_sigaction = SigHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - TEST_PCHECK(sigaction(SIGWINCH, &sa, nullptr) == 0); - MaybeSave(); - - // Indicate to the parent that we're ready. - write_fd.reset(); - - // Wait until we get the signal from the parent. - while (true) { - pause(); - } - } - - ASSERT_THAT(pid, SyscallSucceeds()); - - write_fd.reset(); - - // Wait for the child to indicate that it's unmasked the signal by closing - // the write end. - char buf; - ASSERT_THAT(ReadFd(read_fd.get(), &buf, 1), SyscallSucceedsWithValue(0)); - - // Signal the child and wait for it to die with status 0, indicating that - // it got the expected signal. - EXPECT_THAT(kill(-1, SIGWINCH), SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); -} - -TEST(KillTest, CannotKillInvalidPID) { - // We need an unused pid to verify that kill fails when given one. - // - // There is no way to guarantee that a PID is unused, but the PID of a - // recently exited process likely won't be reused soon. - pid_t fake_pid = fork(); - if (fake_pid == 0) { - _exit(0); - } - - ASSERT_THAT(fake_pid, SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(fake_pid, &status, 0), - SyscallSucceedsWithValue(fake_pid)); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); - - EXPECT_THAT(kill(fake_pid, 0), SyscallFailsWithErrno(ESRCH)); -} - -TEST(KillTest, CannotUseInvalidSignal) { - EXPECT_THAT(kill(getpid(), 200), SyscallFailsWithErrno(EINVAL)); -} - -TEST(KillTest, CanKillRemoteProcess) { - pid_t pid = fork(); - if (pid == 0) { - while (true) { - pause(); - } - } - - ASSERT_THAT(pid, SyscallSucceeds()); - - EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(SIGKILL, WTERMSIG(status)); -} - -TEST(KillTest, CanKillOwnProcess) { - EXPECT_THAT(kill(getpid(), 0), SyscallSucceeds()); -} - -// Verify that you can kill a process even using a tid from a thread other than -// the group leader. -TEST(KillTest, CannotKillTid) { - pid_t tid; - bool tid_available = false; - bool finished = false; - absl::Mutex mu; - ScopedThread t([&] { - mu.Lock(); - tid = gettid(); - tid_available = true; - mu.Await(absl::Condition(&finished)); - mu.Unlock(); - }); - mu.LockWhen(absl::Condition(&tid_available)); - EXPECT_THAT(kill(tid, 0), SyscallSucceeds()); - finished = true; - mu.Unlock(); -} - -TEST(KillTest, SetPgid) { - for (int i = 0; i < 10; i++) { - // The following in the normal pattern for creating a new process group. - // Both the parent and child process will call setpgid in order to avoid any - // race conditions. We do this ten times to catch races. - pid_t pid = fork(); - if (pid == 0) { - setpgid(0, 0); - while (true) { - pause(); - } - } - - ASSERT_THAT(pid, SyscallSucceeds()); - - // Set the child's group and exit. - ASSERT_THAT(setpgid(pid, pid), SyscallSucceeds()); - EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds()); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(-pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(SIGKILL, WTERMSIG(status)); - } -} - -TEST(KillTest, ProcessGroups) { - // Fork a new child. - // - // other_child is used as a placeholder process. We use this PID as our "does - // not exist" process group to ensure some amount of safety. (It is still - // possible to violate this assumption, but extremely unlikely.) - pid_t child = fork(); - if (child == 0) { - while (true) { - pause(); - } - } - ASSERT_THAT(child, SyscallSucceeds()); - - pid_t other_child = fork(); - if (other_child == 0) { - while (true) { - pause(); - } - } - ASSERT_THAT(other_child, SyscallSucceeds()); - - // Ensure the kill does not succeed without the new group. - EXPECT_THAT(kill(-child, SIGKILL), SyscallFailsWithErrno(ESRCH)); - - // Put the child in its own process group. - ASSERT_THAT(setpgid(child, child), SyscallSucceeds()); - - // This should be not allowed: you can only create a new group with the same - // id or join an existing one. The other_child group should not exist. - ASSERT_THAT(setpgid(child, other_child), SyscallFailsWithErrno(EPERM)); - - // Done with other_child; kill it. - EXPECT_THAT(kill(other_child, SIGKILL), SyscallSucceeds()); - int status; - EXPECT_THAT(RetryEINTR(waitpid)(other_child, &status, 0), SyscallSucceeds()); - - // Linux returns success for the no-op call. - ASSERT_THAT(setpgid(child, child), SyscallSucceeds()); - - // Kill the child's process group. - ASSERT_THAT(kill(-child, SIGKILL), SyscallSucceeds()); - - // Wait on the process group; ensure that the signal was as expected. - EXPECT_THAT(RetryEINTR(waitpid)(-child, &status, 0), - SyscallSucceedsWithValue(child)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(SIGKILL, WTERMSIG(status)); - - // Try to kill the process group again; ensure that the wait fails. - EXPECT_THAT(kill(-child, SIGKILL), SyscallFailsWithErrno(ESRCH)); - EXPECT_THAT(RetryEINTR(waitpid)(-child, &status, 0), - SyscallFailsWithErrno(ECHILD)); -} - -TEST(KillTest, ChildDropsPrivsCannotKill) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - const int uid = absl::GetFlag(FLAGS_scratch_uid); - const int gid = absl::GetFlag(FLAGS_scratch_gid); - - // Create the child that drops privileges and tries to kill the parent. - pid_t pid = fork(); - if (pid == 0) { - TEST_PCHECK(setresgid(gid, gid, gid) == 0); - MaybeSave(); - - TEST_PCHECK(setresuid(uid, uid, uid) == 0); - MaybeSave(); - - // setresuid should have dropped CAP_KILL. Make sure. - TEST_CHECK(!HaveCapability(CAP_KILL).ValueOrDie()); - - // Try to kill parent with every signal-sending syscall possible. - pid_t parent = getppid(); - - TEST_CHECK(kill(parent, SIGKILL) < 0); - TEST_PCHECK_MSG(errno == EPERM, "kill failed with wrong errno"); - MaybeSave(); - - TEST_CHECK(tgkill(parent, parent, SIGKILL) < 0); - TEST_PCHECK_MSG(errno == EPERM, "tgkill failed with wrong errno"); - MaybeSave(); - - TEST_CHECK(syscall(SYS_tkill, parent, SIGKILL) < 0); - TEST_PCHECK_MSG(errno == EPERM, "tkill failed with wrong errno"); - MaybeSave(); - - siginfo_t uinfo; - uinfo.si_code = -1; // SI_QUEUE (allowed). - - TEST_CHECK(syscall(SYS_rt_sigqueueinfo, parent, SIGKILL, &uinfo) < 0); - TEST_PCHECK_MSG(errno == EPERM, "rt_sigqueueinfo failed with wrong errno"); - MaybeSave(); - - TEST_CHECK(syscall(SYS_rt_tgsigqueueinfo, parent, parent, SIGKILL, &uinfo) < - 0); - TEST_PCHECK_MSG(errno == EPERM, "rt_sigqueueinfo failed with wrong errno"); - MaybeSave(); - - _exit(0); - } - - ASSERT_THAT(pid, SyscallSucceeds()); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status = " << status; -} - -TEST(KillTest, CanSIGCONTSameSession) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - pid_t stopped_child = fork(); - if (stopped_child == 0) { - raise(SIGSTOP); - _exit(0); - } - - ASSERT_THAT(stopped_child, SyscallSucceeds()); - - // Put the child in its own process group. The child and parent process - // groups also share a session. - ASSERT_THAT(setpgid(stopped_child, stopped_child), SyscallSucceeds()); - - // Make sure child stopped. - int status; - EXPECT_THAT(RetryEINTR(waitpid)(stopped_child, &status, WUNTRACED), - SyscallSucceedsWithValue(stopped_child)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << "status " << status; - - const int uid = absl::GetFlag(FLAGS_scratch_uid); - const int gid = absl::GetFlag(FLAGS_scratch_gid); - - // Drop privileges only in child process, or else this parent process won't be - // able to open some log files after the test ends. - pid_t other_child = fork(); - if (other_child == 0) { - // Drop privileges. - TEST_PCHECK(setresgid(gid, gid, gid) == 0); - MaybeSave(); - - TEST_PCHECK(setresuid(uid, uid, uid) == 0); - MaybeSave(); - - // setresuid should have dropped CAP_KILL. - TEST_CHECK(!HaveCapability(CAP_KILL).ValueOrDie()); - - // Child 2 and child should now not share a thread group and any UIDs. - // Child 2 should have no privileges. That means any signal other than - // SIGCONT should fail. - TEST_CHECK(kill(stopped_child, SIGKILL) < 0); - TEST_PCHECK_MSG(errno == EPERM, "kill failed with wrong errno"); - MaybeSave(); - - TEST_PCHECK(kill(stopped_child, SIGCONT) == 0); - MaybeSave(); - - _exit(0); - } - - ASSERT_THAT(stopped_child, SyscallSucceeds()); - - // Make sure child exited normally. - EXPECT_THAT(RetryEINTR(waitpid)(stopped_child, &status, 0), - SyscallSucceedsWithValue(stopped_child)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; - - // Make sure other_child exited normally. - EXPECT_THAT(RetryEINTR(waitpid)(other_child, &status, 0), - SyscallSucceedsWithValue(other_child)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc deleted file mode 100644 index 4f9ca1a65..000000000 --- a/test/syscalls/linux/link.cc +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/strings/str_cat.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(int32_t, scratch_uid, 65534, "scratch UID"); - -namespace gvisor { -namespace testing { - -namespace { - -// IsSameFile returns true if both filenames have the same device and inode. -bool IsSameFile(const std::string& f1, const std::string& f2) { - // Use lstat rather than stat, so that symlinks are not followed. - struct stat stat1 = {}; - EXPECT_THAT(lstat(f1.c_str(), &stat1), SyscallSucceeds()); - struct stat stat2 = {}; - EXPECT_THAT(lstat(f2.c_str(), &stat2), SyscallSucceeds()); - - return stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino; -} - -// TODO(b/178640646): Add test for linkat with AT_EMPTY_PATH - -TEST(LinkTest, CanCreateLinkFile) { - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string newname = NewTempAbsPath(); - - // Get the initial link count. - uint64_t initial_link_count = - ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path())); - - EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), SyscallSucceeds()); - - EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); - - // Link count should be incremented. - EXPECT_THAT(Links(oldfile.path()), - IsPosixErrorOkAndHolds(initial_link_count + 1)); - - // Delete the link. - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); - - // Link count should be back to initial. - EXPECT_THAT(Links(oldfile.path()), - IsPosixErrorOkAndHolds(initial_link_count)); -} - -TEST(LinkTest, PermissionDenied) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_FOWNER))); - - // Make the file "unsafe" to link by making it only readable, but not - // writable. - const auto unwriteable_file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400)); - const std::string special_path = NewTempAbsPath(); - ASSERT_THAT(mkfifo(special_path.c_str(), 0666), SyscallSucceeds()); - const auto setuid_file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666 | S_ISUID)); - - const std::string newname = NewTempAbsPath(); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting this - // test. Otherwise, the files are created by root (UID before the test), but - // cannot be opened by the `uid` set below after the test. After calling - // setuid(non-zero-UID), there is no way to get root privileges back. - ScopedThread([&] { - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. POSIX threads, however, require that all - // threads have the same UIDs, so using the setuid wrapper sets all threads' - // real UID. - // Also drops capabilities. - EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)), - SyscallSucceeds()); - - EXPECT_THAT(link(unwriteable_file.path().c_str(), newname.c_str()), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(link(special_path.c_str(), newname.c_str()), - SyscallFailsWithErrno(EPERM)); - if (!IsRunningWithVFS1()) { - EXPECT_THAT(link(setuid_file.path().c_str(), newname.c_str()), - SyscallFailsWithErrno(EPERM)); - } - }); -} - -TEST(LinkTest, CannotLinkDirectory) { - auto olddir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string newdir = NewTempAbsPath(); - - EXPECT_THAT(link(olddir.path().c_str(), newdir.c_str()), - SyscallFailsWithErrno(EPERM)); - - EXPECT_THAT(rmdir(olddir.path().c_str()), SyscallSucceeds()); -} - -TEST(LinkTest, CannotLinkWithSlash) { - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - // Put a final "/" on newname. - const std::string newname = absl::StrCat(NewTempAbsPath(), "/"); - - EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), - SyscallFailsWithErrno(ENOENT)); -} - -TEST(LinkTest, OldnameIsEmpty) { - const std::string newname = NewTempAbsPath(); - EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); -} - -TEST(LinkTest, OldnameDoesNotExist) { - const std::string oldname = NewTempAbsPath(); - const std::string newname = NewTempAbsPath(); - EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); -} - -TEST(LinkTest, NewnameCannotExist) { - const std::string newname = - JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo"); - EXPECT_THAT(link("/thisdoesnotmatter", newname.c_str()), - SyscallFailsWithErrno(ENOENT)); -} - -TEST(LinkTest, WithOldDirFD) { - const std::string oldname_parent = NewTempAbsPath(); - const std::string oldname_base = "child"; - const std::string oldname = JoinPath(oldname_parent, oldname_base); - const std::string newname = NewTempAbsPath(); - - // Create oldname_parent directory, and get an FD. - ASSERT_THAT(mkdir(oldname_parent.c_str(), 0777), SyscallSucceeds()); - const FileDescriptor oldname_parent_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(oldname_parent, O_DIRECTORY | O_RDONLY)); - - // Create oldname file. - const FileDescriptor oldname_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(oldname, O_CREAT | O_RDWR, 0666)); - - // Link oldname to newname, using oldname_parent_fd. - EXPECT_THAT(linkat(oldname_parent_fd.get(), oldname_base.c_str(), AT_FDCWD, - newname.c_str(), 0), - SyscallSucceeds()); - - EXPECT_TRUE(IsSameFile(oldname, newname)); - - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); - EXPECT_THAT(rmdir(oldname_parent.c_str()), SyscallSucceeds()); -} - -TEST(LinkTest, BogusFlags) { - ASSERT_THAT(linkat(1, "foo", 2, "bar", 3), SyscallFailsWithErrno(EINVAL)); -} - -TEST(LinkTest, WithNewDirFD) { - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string newname_parent = NewTempAbsPath(); - const std::string newname_base = "child"; - const std::string newname = JoinPath(newname_parent, newname_base); - - // Create newname_parent directory, and get an FD. - EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds()); - const FileDescriptor newname_parent_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_RDONLY)); - - // Link newname to oldfile, using newname_parent_fd. - EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(), - newname.c_str(), 0), - SyscallSucceeds()); - - EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); - - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); - EXPECT_THAT(rmdir(newname_parent.c_str()), SyscallSucceeds()); -} - -TEST(LinkTest, RelPathsWithNonDirFDs) { - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Create a file that will be passed as the directory fd for old/new names. - const std::string filename = NewTempAbsPath(); - const FileDescriptor file_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666)); - - // Using file_fd as olddirfd will fail. - EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0), - SyscallFailsWithErrno(ENOTDIR)); - - // Using file_fd as newdirfd will fail. - EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0), - SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(LinkTest, AbsPathsWithNonDirFDs) { - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string newname = NewTempAbsPath(); - - // Create a file that will be passed as the directory fd for old/new names. - const std::string filename = NewTempAbsPath(); - const FileDescriptor file_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT | O_RDWR, 0666)); - - // Using file_fd as the dirfds is OK as long as paths are absolute. - EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(), - newname.c_str(), 0), - SyscallSucceeds()); -} - -TEST(LinkTest, NewDirFDWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string newname_parent = NewTempAbsPath(); - const std::string newname_base = "child"; - const std::string newname = JoinPath(newname_parent, newname_base); - - // Create newname_parent directory, and get an FD. - EXPECT_THAT(mkdir(newname_parent.c_str(), 0777), SyscallSucceeds()); - const FileDescriptor newname_parent_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(newname_parent, O_DIRECTORY | O_PATH)); - - // Link newname to oldfile, using newname_parent_fd. - EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), newname_parent_fd.get(), - newname.c_str(), 0), - SyscallSucceeds()); - - EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); -} - -TEST(LinkTest, RelPathsNonDirFDsWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Create a file that will be passed as the directory fd for old/new names. - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - - // Using file_fd as olddirfd will fail. - EXPECT_THAT(linkat(file_fd.get(), "foo", AT_FDCWD, "bar", 0), - SyscallFailsWithErrno(ENOTDIR)); - - // Using file_fd as newdirfd will fail. - EXPECT_THAT(linkat(AT_FDCWD, oldfile.path().c_str(), file_fd.get(), "bar", 0), - SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(LinkTest, AbsPathsNonDirFDsWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string newname = NewTempAbsPath(); - - // Create a file that will be passed as the directory fd for old/new names. - TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor file_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); - - // Using file_fd as the dirfds is OK as long as paths are absolute. - EXPECT_THAT(linkat(file_fd.get(), oldfile.path().c_str(), file_fd.get(), - newname.c_str(), 0), - SyscallSucceeds()); -} - -TEST(LinkTest, LinkDoesNotFollowSymlinks) { - // Create oldfile, and oldsymlink which points to it. - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string oldsymlink = NewTempAbsPath(); - EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), - SyscallSucceeds()); - - // Now hard link newname to oldsymlink. - const std::string newname = NewTempAbsPath(); - EXPECT_THAT(link(oldsymlink.c_str(), newname.c_str()), SyscallSucceeds()); - - // The link should not have resolved the symlink, so newname and oldsymlink - // are the same. - EXPECT_TRUE(IsSameFile(oldsymlink, newname)); - EXPECT_FALSE(IsSameFile(oldfile.path(), newname)); - - EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); -} - -TEST(LinkTest, LinkatDoesNotFollowSymlinkByDefault) { - // Create oldfile, and oldsymlink which points to it. - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string oldsymlink = NewTempAbsPath(); - EXPECT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), - SyscallSucceeds()); - - // Now hard link newname to oldsymlink. - const std::string newname = NewTempAbsPath(); - EXPECT_THAT( - linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), 0), - SyscallSucceeds()); - - // The link should not have resolved the symlink, so newname and oldsymlink - // are the same. - EXPECT_TRUE(IsSameFile(oldsymlink, newname)); - EXPECT_FALSE(IsSameFile(oldfile.path(), newname)); - - EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); -} - -TEST(LinkTest, LinkatWithSymlinkFollow) { - // Create oldfile, and oldsymlink which points to it. - auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string oldsymlink = NewTempAbsPath(); - ASSERT_THAT(symlink(oldfile.path().c_str(), oldsymlink.c_str()), - SyscallSucceeds()); - - // Now hard link newname to oldsymlink, and pass AT_SYMLINK_FOLLOW flag. - const std::string newname = NewTempAbsPath(); - ASSERT_THAT(linkat(AT_FDCWD, oldsymlink.c_str(), AT_FDCWD, newname.c_str(), - AT_SYMLINK_FOLLOW), - SyscallSucceeds()); - - // The link should have resolved the symlink, so oldfile and newname are the - // same. - EXPECT_TRUE(IsSameFile(oldfile.path(), newname)); - EXPECT_FALSE(IsSameFile(oldsymlink, newname)); - - EXPECT_THAT(unlink(oldsymlink.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/lseek.cc b/test/syscalls/linux/lseek.cc deleted file mode 100644 index 6ce1e6cc3..000000000 --- a/test/syscalls/linux/lseek.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(LseekTest, InvalidWhence) { - const std::string kFileData = "hello world\n"; - const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); - - ASSERT_THAT(lseek(fd.get(), 0, -1), SyscallFailsWithErrno(EINVAL)); -} - -TEST(LseekTest, NegativeOffset) { - const std::string kFileData = "hello world\n"; - const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); - - EXPECT_THAT(lseek(fd.get(), -(kFileData.length() + 1), SEEK_CUR), - SyscallFailsWithErrno(EINVAL)); -} - -// A 32-bit off_t is not large enough to represent an offset larger than -// maximum file size on standard file systems, so it isn't possible to cause -// overflow. -#if defined(__x86_64__) || defined(__aarch64__) -TEST(LseekTest, Overflow) { - // HA! Classic Linux. We really should have an EOVERFLOW - // here, since we're seeking to something that cannot be - // represented.. but instead we are given an EINVAL. - const std::string kFileData = "hello world\n"; - const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); - EXPECT_THAT(lseek(fd.get(), 0x7fffffffffffffff, SEEK_END), - SyscallFailsWithErrno(EINVAL)); -} -#endif - -TEST(LseekTest, Set) { - const std::string kFileData = "hello world\n"; - const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); - - char buf = '\0'; - EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, kFileData.c_str()[0]); - EXPECT_THAT(lseek(fd.get(), 6, SEEK_SET), SyscallSucceedsWithValue(6)); - ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, kFileData.c_str()[6]); -} - -TEST(LseekTest, Cur) { - const std::string kFileData = "hello world\n"; - const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); - - char buf = '\0'; - EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, kFileData.c_str()[0]); - EXPECT_THAT(lseek(fd.get(), 3, SEEK_CUR), SyscallSucceedsWithValue(4)); - ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, kFileData.c_str()[4]); -} - -TEST(LseekTest, End) { - const std::string kFileData = "hello world\n"; - const TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kFileData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDWR, 0644)); - - char buf = '\0'; - EXPECT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, kFileData.c_str()[0]); - EXPECT_THAT(lseek(fd.get(), -2, SEEK_END), SyscallSucceedsWithValue(10)); - ASSERT_THAT(read(fd.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, kFileData.c_str()[kFileData.length() - 2]); -} - -TEST(LseekTest, InvalidFD) { - EXPECT_THAT(lseek(-1, 0, SEEK_SET), SyscallFailsWithErrno(EBADF)); -} - -TEST(LseekTest, DirCurEnd) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/tmp", O_RDONLY)); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); -} - -TEST(LseekTest, ProcDir) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self", O_RDONLY)); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); -} - -TEST(LseekTest, ProcFile) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/meminfo", O_RDONLY)); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); -} - -TEST(LseekTest, SysDir) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/sys/devices", O_RDONLY)); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), SyscallSucceeds()); -} - -TEST(LseekTest, SeekCurrentDir) { - // From include/linux/fs.h. - constexpr loff_t MAX_LFS_FILESIZE = 0x7fffffffffffffff; - - char* dir = get_current_dir_name(); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir, O_RDONLY)); - - ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - ASSERT_THAT(lseek(fd.get(), 0, SEEK_END), - // Some filesystems (like ext4) allow lseek(SEEK_END) on a - // directory and return MAX_LFS_FILESIZE, others return EINVAL. - AnyOf(SyscallSucceedsWithValue(MAX_LFS_FILESIZE), - SyscallFailsWithErrno(EINVAL))); - free(dir); -} - -TEST(LseekTest, ProcStatTwice) { - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); - - ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - ASSERT_THAT(lseek(fd1.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); - // Check that just because we moved fd1, fd2 doesn't move. - ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - const FileDescriptor fd3 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/stat", O_RDONLY)); - ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); -} - -TEST(LseekTest, EtcPasswdDup) { - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/etc/passwd", O_RDONLY)); - const FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); - - ASSERT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - ASSERT_THAT(lseek(fd1.get(), 1000, SEEK_CUR), SyscallSucceeds()); - // Check that just because we moved fd1, fd2 doesn't move. - ASSERT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); - - const FileDescriptor fd3 = ASSERT_NO_ERRNO_AND_VALUE(fd1.Dup()); - ASSERT_THAT(lseek(fd3.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(1000)); -} - -// TODO(magi): Add tests where we have donated in sockets. - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc deleted file mode 100644 index 6e714b12c..000000000 --- a/test/syscalls/linux/madvise.cc +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void ExpectAllMappingBytes(Mapping const& m, char c) { - auto const v = m.view(); - for (size_t i = 0; i < kPageSize; i++) { - ASSERT_EQ(v[i], c) << "at offset " << i; - } -} - -// Equivalent to ExpectAllMappingBytes but async-signal-safe and with less -// helpful failure messages. -void CheckAllMappingBytes(Mapping const& m, char c) { - auto const v = m.view(); - for (size_t i = 0; i < kPageSize; i++) { - TEST_CHECK_MSG(v[i] == c, "mapping contains wrong value"); - } -} - -TEST(MadviseDontneedTest, ZerosPrivateAnonPage) { - auto m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - ExpectAllMappingBytes(m, 0); - memset(m.ptr(), 1, m.len()); - ExpectAllMappingBytes(m, 1); - ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); - ExpectAllMappingBytes(m, 0); -} - -TEST(MadviseDontneedTest, ZerosCOWAnonPageInCallerOnly) { - auto m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - ExpectAllMappingBytes(m, 0); - memset(m.ptr(), 2, m.len()); - ExpectAllMappingBytes(m, 2); - - // Do madvise in a child process. - pid_t pid = fork(); - CheckAllMappingBytes(m, 2); - if (pid == 0) { - TEST_PCHECK(madvise(m.ptr(), m.len(), MADV_DONTNEED) == 0); - CheckAllMappingBytes(m, 0); - _exit(0); - } - - ASSERT_THAT(pid, SyscallSucceeds()); - - int status = 0; - ASSERT_THAT(waitpid(-1, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(WEXITSTATUS(status), 0); - // The child's madvise should not have affected the parent. - ExpectAllMappingBytes(m, 2); -} - -TEST(MadviseDontneedTest, DoesNotModifySharedAnonPage) { - auto m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED)); - ExpectAllMappingBytes(m, 0); - memset(m.ptr(), 3, m.len()); - ExpectAllMappingBytes(m, 3); - ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); - ExpectAllMappingBytes(m, 3); -} - -TEST(MadviseDontneedTest, CleansPrivateFilePage) { - TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - /* parent = */ GetAbsoluteTestTmpdir(), - /* content = */ std::string(kPageSize, 4), TempPath::kDefaultFileMode)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); - - Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd.get(), 0)); - ExpectAllMappingBytes(m, 4); - memset(m.ptr(), 5, m.len()); - ExpectAllMappingBytes(m, 5); - ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); - ExpectAllMappingBytes(m, 4); -} - -TEST(MadviseDontneedTest, DoesNotModifySharedFilePage) { - TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - /* parent = */ GetAbsoluteTestTmpdir(), - /* content = */ std::string(kPageSize, 6), TempPath::kDefaultFileMode)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); - - Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); - ExpectAllMappingBytes(m, 6); - memset(m.ptr(), 7, m.len()); - ExpectAllMappingBytes(m, 7); - ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); - ExpectAllMappingBytes(m, 7); -} - -TEST(MadviseDontneedTest, IgnoresPermissions) { - auto m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); - EXPECT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); -} - -TEST(MadviseDontforkTest, AddressLength) { - auto m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); - char* addr = static_cast<char*>(m.ptr()); - - // Address must be page aligned. - EXPECT_THAT(madvise(addr + 1, kPageSize, MADV_DONTFORK), - SyscallFailsWithErrno(EINVAL)); - - // Zero length madvise always succeeds. - EXPECT_THAT(madvise(addr, 0, MADV_DONTFORK), SyscallSucceeds()); - - // Length must not roll over after rounding up. - size_t badlen = std::numeric_limits<std::size_t>::max() - (kPageSize / 2); - EXPECT_THAT(madvise(0, badlen, MADV_DONTFORK), SyscallFailsWithErrno(EINVAL)); - - // Length need not be page aligned - it is implicitly rounded up. - EXPECT_THAT(madvise(addr, 1, MADV_DONTFORK), SyscallSucceeds()); - EXPECT_THAT(madvise(addr, kPageSize, MADV_DONTFORK), SyscallSucceeds()); -} - -TEST(MadviseDontforkTest, DontforkShared) { - // Mmap two shared file-backed pages and MADV_DONTFORK the second page. - TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - /* parent = */ GetAbsoluteTestTmpdir(), - /* content = */ std::string(kPageSize * 2, 2), - TempPath::kDefaultFileMode)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); - - 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 ms2 = - Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize); - m.release(); - - ASSERT_THAT(madvise(ms2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds()); - - const auto rest = [&] { - // First page is mapped in child and modifications are visible to parent - // via the shared mapping. - TEST_CHECK(IsMapped(ms1.addr())); - CheckAllMappingBytes(ms1, 2); - memset(ms1.ptr(), 1, kPageSize); - CheckAllMappingBytes(ms1, 1); - - // Second page must not be mapped in child. - TEST_CHECK(!IsMapped(ms2.addr())); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); - - ExpectAllMappingBytes(ms1, 1); // page contents modified by child. - ExpectAllMappingBytes(ms2, 2); // page contents unchanged. -} - -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 mp2 = - Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize); - const Mapping mp3 = - Mapping(reinterpret_cast<void*>(m.addr() + 2 * kPageSize), kPageSize); - m.release(); - - ASSERT_THAT(madvise(mp2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds()); - - // Verify that all pages are zeroed and memset the first, second and third - // pages to 1, 2, and 3 respectively. - ExpectAllMappingBytes(mp1, 0); - memset(mp1.ptr(), 1, kPageSize); - - ExpectAllMappingBytes(mp2, 0); - memset(mp2.ptr(), 2, kPageSize); - - ExpectAllMappingBytes(mp3, 0); - memset(mp3.ptr(), 3, kPageSize); - - const auto rest = [&] { - // Verify first page is mapped, verify its contents and then modify the - // page. The mapping is private so the modifications are not visible to - // the parent. - TEST_CHECK(IsMapped(mp1.addr())); - CheckAllMappingBytes(mp1, 1); - memset(mp1.ptr(), 11, kPageSize); - CheckAllMappingBytes(mp1, 11); - - // Verify second page is not mapped. - TEST_CHECK(!IsMapped(mp2.addr())); - - // Verify third page is mapped, verify its contents and then modify the - // page. The mapping is private so the modifications are not visible to - // the parent. - TEST_CHECK(IsMapped(mp3.addr())); - CheckAllMappingBytes(mp3, 3); - memset(mp3.ptr(), 13, kPageSize); - CheckAllMappingBytes(mp3, 13); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); - - // The fork and COW by child should not affect the parent mappings. - ExpectAllMappingBytes(mp1, 1); - ExpectAllMappingBytes(mp2, 2); - ExpectAllMappingBytes(mp3, 3); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/membarrier.cc b/test/syscalls/linux/membarrier.cc deleted file mode 100644 index 516956a25..000000000 --- a/test/syscalls/linux/membarrier.cc +++ /dev/null @@ -1,268 +0,0 @@ -// 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 <errno.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <atomic> - -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// This is the classic test case for memory fences on architectures with total -// store ordering; see e.g. Intel SDM Vol. 3A Sec. 8.2.3.4 "Loads May Be -// Reordered with Earlier Stores to Different Locations". In each iteration of -// the test, given two variables X and Y initially set to 0 -// (MembarrierTestSharedState::local_var and remote_var in the code), two -// threads execute as follows: -// -// T1 T2 -// -- -- -// -// X = 1 Y = 1 -// T1fence() T2fence() -// read Y read X -// -// On architectures where memory writes may be locally buffered by each CPU -// (essentially all architectures), if T1fence() and T2fence() are omitted or -// ineffective, it is possible for both T1 and T2 to read 0 because the memory -// write from the other CPU is not yet visible outside that CPU. T1fence() and -// T2fence() are expected to perform the necessary synchronization to restore -// sequential consistency: both threads agree on a order of memory accesses that -// is consistent with program order in each thread, such that at least one -// thread reads 1. -// -// In the NoMembarrier test, T1fence() and T2fence() are both ordinary memory -// fences establishing ordering between memory accesses before and after the -// fence (std::atomic_thread_fence). In all other test cases, T1fence() is not a -// memory fence at all, but only prevents compiler reordering of memory accesses -// (std::atomic_signal_fence); T2fence() is an invocation of the membarrier() -// syscall, which establishes ordering of memory accesses before and after the -// syscall on both threads. - -template <typename F> -int DoMembarrierTestSide(std::atomic<int>* our_var, - std::atomic<int> const& their_var, - F const& test_fence) { - our_var->store(1, std::memory_order_relaxed); - test_fence(); - return their_var.load(std::memory_order_relaxed); -} - -struct MembarrierTestSharedState { - std::atomic<int64_t> remote_iter_cur; - std::atomic<int64_t> remote_iter_done; - std::atomic<int> local_var; - std::atomic<int> remote_var; - int remote_obs_of_local_var; - - void Init() { - remote_iter_cur.store(-1, std::memory_order_relaxed); - remote_iter_done.store(-1, std::memory_order_relaxed); - } -}; - -// Special value for MembarrierTestSharedState::remote_iter_cur indicating that -// the remote thread should terminate. -constexpr int64_t kRemoteIterStop = -2; - -// Must be async-signal-safe. -template <typename F> -void RunMembarrierTestRemoteSide(MembarrierTestSharedState* state, - F const& test_fence) { - int64_t i = 0; - int64_t cur; - while (true) { - while ((cur = state->remote_iter_cur.load(std::memory_order_acquire)) < i) { - if (cur == kRemoteIterStop) { - return; - } - // spin - } - state->remote_obs_of_local_var = - DoMembarrierTestSide(&state->remote_var, state->local_var, test_fence); - state->remote_iter_done.store(i, std::memory_order_release); - i++; - } -} - -template <typename F> -void RunMembarrierTestLocalSide(MembarrierTestSharedState* state, - F const& test_fence) { - // On test completion, instruct the remote thread to terminate. - Cleanup cleanup_remote([&] { - state->remote_iter_cur.store(kRemoteIterStop, std::memory_order_relaxed); - }); - - int64_t i = 0; - absl::Time end = absl::Now() + absl::Seconds(5); // arbitrary test duration - while (absl::Now() < end) { - // Reset both vars to 0. - state->local_var.store(0, std::memory_order_relaxed); - state->remote_var.store(0, std::memory_order_relaxed); - // Instruct the remote thread to begin this iteration. - state->remote_iter_cur.store(i, std::memory_order_release); - // Perform our side of the test. - auto local_obs_of_remote_var = - DoMembarrierTestSide(&state->local_var, state->remote_var, test_fence); - // Wait for the remote thread to finish this iteration. - while (state->remote_iter_done.load(std::memory_order_acquire) < i) { - // spin - } - ASSERT_TRUE(local_obs_of_remote_var != 0 || - state->remote_obs_of_local_var != 0); - i++; - } -} - -TEST(MembarrierTest, NoMembarrier) { - MembarrierTestSharedState state; - state.Init(); - - ScopedThread remote_thread([&] { - RunMembarrierTestRemoteSide( - &state, [] { std::atomic_thread_fence(std::memory_order_seq_cst); }); - }); - RunMembarrierTestLocalSide( - &state, [] { std::atomic_thread_fence(std::memory_order_seq_cst); }); -} - -enum membarrier_cmd { - MEMBARRIER_CMD_QUERY = 0, - MEMBARRIER_CMD_GLOBAL = (1 << 0), - MEMBARRIER_CMD_GLOBAL_EXPEDITED = (1 << 1), - MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED = (1 << 2), - MEMBARRIER_CMD_PRIVATE_EXPEDITED = (1 << 3), - MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED = (1 << 4), -}; - -int membarrier(membarrier_cmd cmd, int flags) { - return syscall(SYS_membarrier, cmd, flags); -} - -PosixErrorOr<int> SupportedMembarrierCommands() { - int cmds = membarrier(MEMBARRIER_CMD_QUERY, 0); - if (cmds < 0) { - if (errno == ENOSYS) { - // No commands are supported. - return 0; - } - return PosixError(errno, "membarrier(MEMBARRIER_CMD_QUERY) failed"); - } - return cmds; -} - -TEST(MembarrierTest, Global) { - SKIP_IF((ASSERT_NO_ERRNO_AND_VALUE(SupportedMembarrierCommands()) & - MEMBARRIER_CMD_GLOBAL) == 0); - - Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED)); - auto state = static_cast<MembarrierTestSharedState*>(m.ptr()); - state->Init(); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - RunMembarrierTestRemoteSide( - state, [] { TEST_PCHECK(membarrier(MEMBARRIER_CMD_GLOBAL, 0) == 0); }); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - Cleanup cleanup_child([&] { - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - }); - RunMembarrierTestLocalSide( - state, [] { std::atomic_signal_fence(std::memory_order_seq_cst); }); -} - -TEST(MembarrierTest, GlobalExpedited) { - constexpr int kRequiredCommands = MEMBARRIER_CMD_GLOBAL_EXPEDITED | - MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED; - SKIP_IF((ASSERT_NO_ERRNO_AND_VALUE(SupportedMembarrierCommands()) & - kRequiredCommands) != kRequiredCommands); - - ASSERT_THAT(membarrier(MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED, 0), - SyscallSucceeds()); - - Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED)); - auto state = static_cast<MembarrierTestSharedState*>(m.ptr()); - state->Init(); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - RunMembarrierTestRemoteSide(state, [] { - TEST_PCHECK(membarrier(MEMBARRIER_CMD_GLOBAL_EXPEDITED, 0) == 0); - }); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - Cleanup cleanup_child([&] { - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - }); - RunMembarrierTestLocalSide( - state, [] { std::atomic_signal_fence(std::memory_order_seq_cst); }); -} - -TEST(MembarrierTest, PrivateExpedited) { - constexpr int kRequiredCommands = MEMBARRIER_CMD_PRIVATE_EXPEDITED | - MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED; - SKIP_IF((ASSERT_NO_ERRNO_AND_VALUE(SupportedMembarrierCommands()) & - kRequiredCommands) != kRequiredCommands); - - ASSERT_THAT(membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0), - SyscallSucceeds()); - - MembarrierTestSharedState state; - state.Init(); - - ScopedThread remote_thread([&] { - RunMembarrierTestRemoteSide(&state, [] { - TEST_PCHECK(membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0) == 0); - }); - }); - RunMembarrierTestLocalSide( - &state, [] { std::atomic_signal_fence(std::memory_order_seq_cst); }); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/memfd.cc b/test/syscalls/linux/memfd.cc deleted file mode 100644 index 4a450742b..000000000 --- a/test/syscalls/linux/memfd.cc +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <linux/memfd.h> -#include <linux/unistd.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/syscall.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -// The header sys/memfd.h isn't available on all systems, so redefining some of -// the constants here. -#define F_LINUX_SPECIFIC_BASE 1024 - -#ifndef F_ADD_SEALS -#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) -#endif /* F_ADD_SEALS */ - -#ifndef F_GET_SEALS -#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) -#endif /* F_GET_SEALS */ - -#define F_SEAL_SEAL 0x0001 -#define F_SEAL_SHRINK 0x0002 -#define F_SEAL_GROW 0x0004 -#define F_SEAL_WRITE 0x0008 - -using ::gvisor::testing::IsTmpfs; -using ::testing::StartsWith; - -const std::string kMemfdName = "some-memfd"; - -int memfd_create(const std::string& name, unsigned int flags) { - return syscall(__NR_memfd_create, name.c_str(), flags); -} - -PosixErrorOr<FileDescriptor> MemfdCreate(const std::string& name, - uint32_t flags) { - int fd = memfd_create(name, flags); - if (fd < 0) { - return PosixError( - errno, absl::StrFormat("memfd_create(\"%s\", %#x)", name, flags)); - } - MaybeSave(); - return FileDescriptor(fd); -} - -// Procfs entries for memfds display the appropriate name. -TEST(MemfdTest, Name) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0)); - const std::string proc_name = ASSERT_NO_ERRNO_AND_VALUE( - ReadLink(absl::StrFormat("/proc/self/fd/%d", memfd.get()))); - EXPECT_THAT(proc_name, StartsWith("/memfd:" + kMemfdName)); -} - -// Memfds support read/write syscalls. -TEST(MemfdTest, WriteRead) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0)); - - // Write a random page of data to the memfd via write(2). - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Read back the same data and verify. - std::vector<char> buf2(kPageSize); - ASSERT_THAT(lseek(memfd.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(read(memfd.get(), buf2.data(), buf2.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(buf, buf2); -} - -// Memfds can be mapped and used as usual. -TEST(MemfdTest, Mmap) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0)); - const Mapping m1 = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0)); - - // Write a random page of data to the memfd via mmap m1. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - memcpy(m1.ptr(), buf.data(), buf.size()); - - // Read the data back via a read syscall on the memfd. - std::vector<char> buf2(kPageSize); - EXPECT_THAT(read(memfd.get(), buf2.data(), buf2.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(buf, buf2); - - // The same data should be accessible via a new mapping m2. - const Mapping m2 = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0)); - EXPECT_EQ(0, memcmp(m1.ptr(), m2.ptr(), kPageSize)); -} - -TEST(MemfdTest, DuplicateFDsShareContent) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0)); - const Mapping m1 = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0)); - const FileDescriptor memfd2 = ASSERT_NO_ERRNO_AND_VALUE(memfd.Dup()); - - // Write a random page of data to the memfd via mmap m1. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - memcpy(m1.ptr(), buf.data(), buf.size()); - - // Read the data back via a read syscall on a duplicate fd. - std::vector<char> buf2(kPageSize); - EXPECT_THAT(read(memfd2.get(), buf2.data(), buf2.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(buf, buf2); -} - -// File seals are disabled by default on memfds. -TEST(MemfdTest, SealingDisabledByDefault) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0)); - EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_SEAL)); - // Attempting to set any seal should fail. - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), - SyscallFailsWithErrno(EPERM)); -} - -// Seals can be retrieved and updated for memfds. -TEST(MemfdTest, SealsGetSet) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - int seals; - ASSERT_THAT(seals = fcntl(memfd.get(), F_GET_SEALS), SyscallSucceeds()); - // No seals are set yet. - EXPECT_EQ(0, seals); - - // Set a seal and check that we can get it back. - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds()); - EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_WRITE)); - - // Set some more seals and verify. - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK), - SyscallSucceeds()); - EXPECT_THAT( - fcntl(memfd.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK)); - - // Attempting to set a seal that is already set is a no-op. - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds()); - EXPECT_THAT( - fcntl(memfd.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK)); - - // Add remaining seals and verify. - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SEAL), SyscallSucceeds()); - EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_WRITE | F_SEAL_GROW | - F_SEAL_SHRINK | F_SEAL_SEAL)); -} - -// F_SEAL_GROW prevents a memfd from being grown using ftruncate. -TEST(MemfdTest, SealGrowWithTruncate) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds()); - - // Try grow the memfd by 1 page. - ASSERT_THAT(ftruncate(memfd.get(), kPageSize * 2), - SyscallFailsWithErrno(EPERM)); - - // Ftruncate calls that don't actually grow the memfd are allowed. - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize / 2), SyscallSucceeds()); - - // After shrinking, growing back is not allowed. - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallFailsWithErrno(EPERM)); -} - -// F_SEAL_GROW prevents a memfd from being grown using the write syscall. -TEST(MemfdTest, SealGrowWithWrite) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - - // Initially, writing to the memfd succeeds. - const std::vector<char> buf(kPageSize); - EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Apply F_SEAL_GROW, subsequent writes which extend the memfd should fail. - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds()); - EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EPERM)); - - // However, zero-length writes are ok since they don't grow the memfd. - EXPECT_THAT(write(memfd.get(), buf.data(), 0), SyscallSucceeds()); - - // Writing to existing parts of the memfd is also ok. - ASSERT_THAT(lseek(memfd.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Returning the end of the file and writing still not allowed. - EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EPERM)); -} - -// F_SEAL_GROW causes writes which partially extend off the current EOF to -// partially succeed, up to the page containing the EOF. -TEST(MemfdTest, SealGrowPartialWriteTruncated) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds()); - - // FD offset: 1 page, EOF: 1 page. - - ASSERT_THAT(lseek(memfd.get(), kPageSize * 3 / 4, SEEK_SET), - SyscallSucceeds()); - - // FD offset: 3/4 page. Writing a full page now should only write 1/4 page - // worth of data. This partially succeeds because the first page is entirely - // within the file and requires no growth, but attempting to write the final - // 3/4 page would require growing the file. - const std::vector<char> buf(kPageSize); - EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize / 4)); -} - -// F_SEAL_GROW causes writes which partially extend off the current EOF to fail -// in its entirety if the only data written would be to the page containing the -// EOF. -TEST(MemfdTest, SealGrowPartialWriteTruncatedSamePage) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize * 3 / 4), SyscallSucceeds()); - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds()); - - // EOF: 3/4 page, writing 1/2 page starting at 1/2 page would cause the file - // to grow. Since this would require only the page containing the EOF to be - // modified, the write is rejected entirely. - const std::vector<char> buf(kPageSize / 2); - EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size(), kPageSize / 2), - SyscallFailsWithErrno(EPERM)); - - // However, writing up to EOF is fine. - EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size() / 2, kPageSize / 2), - SyscallSucceedsWithValue(kPageSize / 4)); -} - -// F_SEAL_SHRINK prevents a memfd from being shrunk using ftruncate. -TEST(MemfdTest, SealShrink) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SHRINK), - SyscallSucceeds()); - - // Shrink by half a page. - ASSERT_THAT(ftruncate(memfd.get(), kPageSize / 2), - SyscallFailsWithErrno(EPERM)); - - // Ftruncate calls that don't actually shrink the file are allowed. - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds()); - ASSERT_THAT(ftruncate(memfd.get(), kPageSize * 2), SyscallSucceeds()); - - // After growing, shrinking is still not allowed. - ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallFailsWithErrno(EPERM)); -} - -// F_SEAL_WRITE prevents a memfd from being written to through a write -// syscall. -TEST(MemfdTest, SealWriteWithWrite) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - const std::vector<char> buf(kPageSize); - ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds()); - - // Attemping to write at the end of the file fails. - EXPECT_THAT(write(memfd.get(), buf.data(), 1), SyscallFailsWithErrno(EPERM)); - - // Attemping to overwrite an existing part of the memfd fails. - EXPECT_THAT(pwrite(memfd.get(), buf.data(), 1, 0), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size() / 2, kPageSize / 2), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size(), kPageSize / 2), - SyscallFailsWithErrno(EPERM)); - - // Zero-length writes however do not fail. - EXPECT_THAT(write(memfd.get(), buf.data(), 0), SyscallSucceeds()); -} - -// F_SEAL_WRITE prevents a memfd from being written to through an mmap. -TEST(MemfdTest, SealWriteWithMmap) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - const std::vector<char> buf(kPageSize); - ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds()); - - // Can't create a shared mapping with writes sealed. - void* ret = mmap(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, memfd.get(), 0); - EXPECT_EQ(ret, MAP_FAILED); - EXPECT_EQ(errno, EPERM); - ret = mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, memfd.get(), 0); - EXPECT_EQ(ret, MAP_FAILED); - EXPECT_EQ(errno, EPERM); - - // However, private mappings are ok. - EXPECT_NO_ERRNO(Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, - memfd.get(), 0)); -} - -// Adding F_SEAL_WRITE fails when there are outstanding writable mappings to a -// memfd. -TEST(MemfdTest, SealWriteWithOutstandingWritbleMapping) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - const std::vector<char> buf(kPageSize); - ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Attempting to add F_SEAL_WRITE with active shared mapping with any set of - // permissions fails. - - // Read-only shared mapping. - { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, memfd.get(), 0)); - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), - SyscallFailsWithErrno(EBUSY)); - } - - // Write-only shared mapping. - { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, memfd.get(), 0)); - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), - SyscallFailsWithErrno(EBUSY)); - } - - // Read-write shared mapping. - { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - memfd.get(), 0)); - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), - SyscallFailsWithErrno(EBUSY)); - } - - // F_SEAL_WRITE can be set with private mappings with any permissions. - { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, - memfd.get(), 0)); - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), - SyscallSucceeds()); - } -} - -// When applying F_SEAL_WRITE fails due to outstanding writable mappings, any -// additional seals passed to the same add seal call are also rejected. -TEST(MemfdTest, NoPartialSealApplicationWhenWriteSealRejected) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0)); - - // Try add some seals along with F_SEAL_WRITE. The seal application should - // fail since there exists an active shared mapping. - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW), - SyscallFailsWithErrno(EBUSY)); - - // None of the seals should be applied. - EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS), SyscallSucceedsWithValue(0)); -} - -// Seals are inode level properties, and apply to all file descriptors referring -// to a memfd. -TEST(MemfdTest, SealsAreInodeLevelProperties) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - const FileDescriptor memfd2 = ASSERT_NO_ERRNO_AND_VALUE(memfd.Dup()); - - // Add seal through the original memfd, and verify that it appears on the - // dupped fd. - ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds()); - EXPECT_THAT(fcntl(memfd2.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_WRITE)); - - // Verify the seal actually applies to both fds. - std::vector<char> buf(kPageSize); - EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(write(memfd2.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EPERM)); - - // Seals are enforced on new FDs that are dupped after the seal is already - // applied. - const FileDescriptor memfd3 = ASSERT_NO_ERRNO_AND_VALUE(memfd2.Dup()); - EXPECT_THAT(write(memfd3.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EPERM)); - - // Try a new seal applied to one of the dupped fds. - ASSERT_THAT(fcntl(memfd3.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds()); - EXPECT_THAT(ftruncate(memfd.get(), kPageSize), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(ftruncate(memfd2.get(), kPageSize), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(ftruncate(memfd3.get(), kPageSize), SyscallFailsWithErrno(EPERM)); -} - -// Tmpfs files also support seals, but are created with F_SEAL_SEAL. -TEST(MemfdTest, TmpfsFilesHaveSealSeal) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs("/tmp"))); - const TempPath tmpfs_file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn("/tmp")); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfs_file.path(), O_RDWR, 0644)); - EXPECT_THAT(fcntl(fd.get(), F_GET_SEALS), - SyscallSucceedsWithValue(F_SEAL_SEAL)); -} - -// Can open a memfd from procfs and use as normal. -TEST(MemfdTest, CanOpenFromProcfs) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - - // Write a random page of data to the memfd via write(2). - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Read back the same data from the fd obtained from procfs and verify. - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(absl::StrFormat("/proc/self/fd/%d", memfd.get()), O_RDWR)); - std::vector<char> buf2(kPageSize); - EXPECT_THAT(pread(fd.get(), buf2.data(), buf2.size(), 0), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(buf, buf2); -} - -// Test that memfd permissions are set up correctly to allow another process to -// open it from procfs. -TEST(MemfdTest, OtherProcessCanOpenFromProcfs) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - const auto memfd_path = - absl::StrFormat("/proc/%d/fd/%d", getpid(), memfd.get()); - const auto rest = [&] { - int fd = open(memfd_path.c_str(), O_RDWR); - TEST_PCHECK(fd >= 0); - TEST_PCHECK(close(fd) >= 0); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -// Test that only files opened as writable can have seals applied to them. -// Normally there's no way to specify file permissions on memfds, but we can -// obtain a read-only memfd by opening the corresponding procfs fd entry as -// read-only. -TEST(MemfdTest, MemfdMustBeWritableToModifySeals) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING)); - - // Initially adding a seal works. - EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds()); - - // Re-open the memfd as read-only from procfs. - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(absl::StrFormat("/proc/self/fd/%d", memfd.get()), O_RDONLY)); - - // Can't add seals through an unwritable fd. - EXPECT_THAT(fcntl(fd.get(), F_ADD_SEALS, F_SEAL_GROW), - SyscallFailsWithErrno(EPERM)); -} - -// Test that the memfd implementation internally tracks potentially writable -// maps correctly. -TEST(MemfdTest, MultipleWritableAndNonWritableRefsToSameFileRegion) { - const FileDescriptor memfd = - ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0)); - - // Populate with a random page of data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Read-only map to the page. This should cause an initial mapping to be - // created. - Mapping m1 = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ, MAP_PRIVATE, memfd.get(), 0)); - - // Create a shared writable map to the page. This should cause the internal - // mapping to become potentially writable. - Mapping m2 = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0)); - - // Drop the read-only mapping first. If writable-ness isn't tracked correctly, - // this can cause some misaccounting, which can trigger asserts internally. - m1.reset(); - m2.reset(); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/memory_accounting.cc b/test/syscalls/linux/memory_accounting.cc deleted file mode 100644 index 94aea4077..000000000 --- a/test/syscalls/linux/memory_accounting.cc +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/mman.h> - -#include <map> - -#include "gtest/gtest.h" -#include "absl/strings/match.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_format.h" -#include "absl/strings/str_split.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using ::absl::StrFormat; - -// AnonUsageFromMeminfo scrapes the current anonymous memory usage from -// /proc/meminfo and returns it in bytes. -PosixErrorOr<uint64_t> AnonUsageFromMeminfo() { - ASSIGN_OR_RETURN_ERRNO(auto meminfo, GetContents("/proc/meminfo")); - std::vector<std::string> lines(absl::StrSplit(meminfo, '\n')); - - // Try to find AnonPages line, the format is AnonPages:\\s+(\\d+) kB\n. - for (const auto& line : lines) { - if (!absl::StartsWith(line, "AnonPages:")) { - continue; - } - - std::vector<std::string> parts( - absl::StrSplit(line, ' ', absl::SkipEmpty())); - if (parts.size() == 3) { - // The size is the second field, let's try to parse it as a number. - ASSIGN_OR_RETURN_ERRNO(auto anon_kb, Atoi<uint64_t>(parts[1])); - return anon_kb * 1024; - } - - return PosixError(EINVAL, "AnonPages field in /proc/meminfo was malformed"); - } - - return PosixError(EINVAL, "AnonPages field not found in /proc/meminfo"); -} - -TEST(MemoryAccounting, AnonAccountingPreservedOnSaveRestore) { - // This test isn't meaningful on Linux. /proc/meminfo reports system-wide - // memory usage, which can change arbitrarily in Linux from other activity on - // the machine. In gvisor, this test is the only thing running on the - // "machine", so values in /proc/meminfo accurately reflect the memory used by - // the test. - SKIP_IF(!IsRunningOnGvisor()); - - uint64_t anon_initial = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); - - // Cause some anonymous memory usage. - uint64_t map_bytes = Megabytes(512); - char* mem = - static_cast<char*>(mmap(nullptr, map_bytes, PROT_READ | PROT_WRITE, - MAP_POPULATE | MAP_ANON | MAP_PRIVATE, -1, 0)); - ASSERT_NE(mem, MAP_FAILED) - << "Map failed, errno: " << errno << " (" << strerror(errno) << ")."; - - // Write something to each page to prevent them from being decommited on - // S/R. Zero pages are dropped on save. - for (uint64_t i = 0; i < map_bytes; i += kPageSize) { - mem[i] = 'a'; - } - - uint64_t anon_after_alloc = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); - EXPECT_THAT(anon_after_alloc, - EquivalentWithin(anon_initial + map_bytes, 0.03)); - - // We have many implicit S/R cycles from scraping /proc/meminfo throughout the - // test, but throw an explicit S/R in here as well. - MaybeSave(); - - // Usage should remain the same across S/R. - uint64_t anon_after_sr = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); - EXPECT_THAT(anon_after_sr, EquivalentWithin(anon_after_alloc, 0.03)); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc deleted file mode 100644 index 059fad598..000000000 --- a/test/syscalls/linux/mempolicy.cc +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sys/syscall.h> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/util/cleanup.h" -#include "test/util/memory_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#define BITS_PER_BYTE 8 - -#define MPOL_F_STATIC_NODES (1 << 15) -#define MPOL_F_RELATIVE_NODES (1 << 14) -#define MPOL_DEFAULT 0 -#define MPOL_PREFERRED 1 -#define MPOL_BIND 2 -#define MPOL_INTERLEAVE 3 -#define MPOL_LOCAL 4 -#define MPOL_F_NODE (1 << 0) -#define MPOL_F_ADDR (1 << 1) -#define MPOL_F_MEMS_ALLOWED (1 << 2) -#define MPOL_MF_STRICT (1 << 0) -#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 flags) { - return syscall(SYS_get_mempolicy, policy, nmask, maxnode, addr, flags); -} - -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, - unsigned flags) { - return syscall(SYS_mbind, addr, len, mode, nodemask, maxnode, flags); -} - -// Creates a cleanup object that resets the calling thread's mempolicy to the -// system default when the calling scope ends. -Cleanup ScopedMempolicy() { - return Cleanup([] { - EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, nullptr, 0), SyscallSucceeds()); - }); -} - -// Temporarily change the memory policy for the calling thread within the -// caller's scope. -PosixErrorOr<Cleanup> ScopedSetMempolicy(int mode, uint64_t* nmask, - uint64_t maxnode) { - if (set_mempolicy(mode, nmask, maxnode)) { - return PosixError(errno, "set_mempolicy"); - } - return ScopedMempolicy(); -} - -TEST(MempolicyTest, CheckDefaultPolicy) { - int mode = 0; - uint64_t nodemask = 0; - ASSERT_THAT(get_mempolicy(&mode, &nodemask, sizeof(nodemask) * BITS_PER_BYTE, - nullptr, 0), - SyscallSucceeds()); - - EXPECT_EQ(MPOL_DEFAULT, mode); - EXPECT_EQ(0x0, nodemask); -} - -TEST(MempolicyTest, PolicyPreservedAfterSetMempolicy) { - uint64_t nodemask = 0x1; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( - MPOL_BIND, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); - - int mode = 0; - uint64_t nodemask_after = 0x0; - ASSERT_THAT(get_mempolicy(&mode, &nodemask_after, - sizeof(nodemask_after) * BITS_PER_BYTE, nullptr, 0), - SyscallSucceeds()); - EXPECT_EQ(MPOL_BIND, mode); - EXPECT_EQ(0x1, nodemask_after); - - // Try throw in some mode flags. - for (auto mode_flag : {MPOL_F_STATIC_NODES, MPOL_F_RELATIVE_NODES}) { - auto cleanup2 = ASSERT_NO_ERRNO_AND_VALUE( - ScopedSetMempolicy(MPOL_INTERLEAVE | mode_flag, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE)); - mode = 0; - nodemask_after = 0x0; - ASSERT_THAT( - get_mempolicy(&mode, &nodemask_after, - sizeof(nodemask_after) * BITS_PER_BYTE, nullptr, 0), - SyscallSucceeds()); - EXPECT_EQ(MPOL_INTERLEAVE | mode_flag, mode); - EXPECT_EQ(0x1, nodemask_after); - } -} - -TEST(MempolicyTest, SetMempolicyRejectsInvalidInputs) { - auto cleanup = ScopedMempolicy(); - uint64_t nodemask; - - if (IsRunningOnGvisor()) { - // Invalid nodemask, we only support a single node on gvisor. - nodemask = 0x4; - ASSERT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); - } - - nodemask = 0x1; - - // Invalid mode. - ASSERT_THAT(set_mempolicy(7439, &nodemask, sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); - - // Invalid nodemask size. - ASSERT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); - - // Invalid mode flag. - ASSERT_THAT( - set_mempolicy(MPOL_DEFAULT | MPOL_F_STATIC_NODES | MPOL_F_RELATIVE_NODES, - &nodemask, sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); - - // MPOL_INTERLEAVE with empty nodemask. - nodemask = 0x0; - ASSERT_THAT(set_mempolicy(MPOL_INTERLEAVE, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE), - SyscallFailsWithErrno(EINVAL)); -} - -// The manpages specify that the nodemask provided to set_mempolicy are -// considered empty if the nodemask pointer is null, or if the nodemask size is -// 0. We use a policy which accepts both empty and non-empty nodemasks -// (MPOL_PREFERRED), a policy which requires a non-empty nodemask (MPOL_BIND), -// and a policy which completely ignores the nodemask (MPOL_DEFAULT) to verify -// argument checking around nodemasks. -TEST(MempolicyTest, EmptyNodemaskOnSet) { - auto cleanup = ScopedMempolicy(); - - EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, nullptr, 1), SyscallSucceeds()); - EXPECT_THAT(set_mempolicy(MPOL_BIND, nullptr, 1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(set_mempolicy(MPOL_PREFERRED, nullptr, 1), SyscallSucceeds()); - - uint64_t nodemask = 0x1; - EXPECT_THAT(set_mempolicy(MPOL_DEFAULT, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(set_mempolicy(MPOL_BIND, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(set_mempolicy(MPOL_PREFERRED, &nodemask, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(MempolicyTest, QueryAvailableNodes) { - uint64_t nodemask = 0; - ASSERT_THAT( - get_mempolicy(nullptr, &nodemask, sizeof(nodemask) * BITS_PER_BYTE, - nullptr, MPOL_F_MEMS_ALLOWED), - SyscallSucceeds()); - // We can only be sure there is a single node if running on gvisor. - if (IsRunningOnGvisor()) { - EXPECT_EQ(0x1, nodemask); - } - - // MPOL_F_ADDR and MPOL_F_NODE flags may not be combined with - // MPOL_F_MEMS_ALLLOWED. - for (auto flags : - {MPOL_F_MEMS_ALLOWED | MPOL_F_ADDR, MPOL_F_MEMS_ALLOWED | MPOL_F_NODE, - MPOL_F_MEMS_ALLOWED | MPOL_F_ADDR | MPOL_F_NODE}) { - ASSERT_THAT(get_mempolicy(nullptr, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE, nullptr, flags), - SyscallFailsWithErrno(EINVAL)); - } -} - -TEST(MempolicyTest, GetMempolicyQueryNodeForAddress) { - uint64_t dummy_stack_address; - auto dummy_heap_address = absl::make_unique<uint64_t>(); - int mode; - - for (auto ptr : {&dummy_stack_address, dummy_heap_address.get()}) { - mode = -1; - ASSERT_THAT( - get_mempolicy(&mode, nullptr, 0, ptr, MPOL_F_ADDR | MPOL_F_NODE), - SyscallSucceeds()); - // If we're not running on gvisor, the address may be allocated on a - // different numa node. - if (IsRunningOnGvisor()) { - EXPECT_EQ(0, mode); - } - } - - void* invalid_address = reinterpret_cast<void*>(-1); - - // Invalid address. - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, invalid_address, - MPOL_F_ADDR | MPOL_F_NODE), - SyscallFailsWithErrno(EFAULT)); - - // Invalid mode pointer. - ASSERT_THAT(get_mempolicy(reinterpret_cast<int*>(invalid_address), nullptr, 0, - &dummy_stack_address, MPOL_F_ADDR | MPOL_F_NODE), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(MempolicyTest, GetMempolicyCanOmitPointers) { - int mode; - uint64_t nodemask; - - // Omit nodemask pointer. - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, 0), SyscallSucceeds()); - // Omit mode pointer. - ASSERT_THAT(get_mempolicy(nullptr, &nodemask, - sizeof(nodemask) * BITS_PER_BYTE, nullptr, 0), - SyscallSucceeds()); - // Omit both pointers. - ASSERT_THAT(get_mempolicy(nullptr, nullptr, 0, nullptr, 0), - SyscallSucceeds()); -} - -TEST(MempolicyTest, GetMempolicyNextInterleaveNode) { - int mode; - // Policy for thread not yet set to MPOL_INTERLEAVE, can't query for - // the next node which will be used for allocation. - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, MPOL_F_NODE), - SyscallFailsWithErrno(EINVAL)); - - // Set default policy for thread to MPOL_INTERLEAVE. - uint64_t nodemask = 0x1; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy( - MPOL_INTERLEAVE, &nodemask, sizeof(nodemask) * BITS_PER_BYTE)); - - mode = -1; - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, nullptr, MPOL_F_NODE), - SyscallSucceeds()); - EXPECT_EQ(0, mode); -} - -TEST(MempolicyTest, Mbind) { - // Temporarily set the thread policy to MPOL_PREFERRED. - const auto cleanup_thread_policy = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetMempolicy(MPOL_PREFERRED, nullptr, 0)); - - const auto mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS)); - - // vmas default to MPOL_DEFAULT irrespective of the thread policy (currently - // MPOL_PREFERRED). - int mode; - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, mapping.ptr(), MPOL_F_ADDR), - SyscallSucceeds()); - EXPECT_EQ(mode, MPOL_DEFAULT); - - // Set MPOL_PREFERRED for the vma and read it back. - ASSERT_THAT( - mbind(mapping.ptr(), mapping.len(), MPOL_PREFERRED, nullptr, 0, 0), - SyscallSucceeds()); - ASSERT_THAT(get_mempolicy(&mode, nullptr, 0, mapping.ptr(), MPOL_F_ADDR), - SyscallSucceeds()); - EXPECT_EQ(mode, MPOL_PREFERRED); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mincore.cc b/test/syscalls/linux/mincore.cc deleted file mode 100644 index 5c1240c89..000000000 --- a/test/syscalls/linux/mincore.cc +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <stdint.h> -#include <string.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <algorithm> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -size_t CountSetLSBs(std::vector<unsigned char> const& vec) { - return std::count_if(begin(vec), end(vec), - [](unsigned char c) { return (c & 1) != 0; }); -} - -TEST(MincoreTest, DirtyAnonPagesAreResident) { - constexpr size_t kTestPageCount = 10; - auto const kTestMappingBytes = kTestPageCount * kPageSize; - auto m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kTestMappingBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - memset(m.ptr(), 0, m.len()); - - std::vector<unsigned char> vec(kTestPageCount, 0); - ASSERT_THAT(mincore(m.ptr(), kTestMappingBytes, vec.data()), - SyscallSucceeds()); - EXPECT_EQ(kTestPageCount, CountSetLSBs(vec)); -} - -TEST(MincoreTest, UnalignedAddressFails) { - // Map and touch two pages, then try to mincore the second half of the first - // page + the first half of the second page. Both pages are mapped, but - // mincore should return EINVAL due to the misaligned start address. - constexpr size_t kTestPageCount = 2; - auto const kTestMappingBytes = kTestPageCount * kPageSize; - auto m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kTestMappingBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - memset(m.ptr(), 0, m.len()); - - std::vector<unsigned char> vec(kTestPageCount, 0); - EXPECT_THAT(mincore(reinterpret_cast<void*>(m.addr() + kPageSize / 2), - kPageSize, vec.data()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(MincoreTest, UnalignedLengthSucceedsAndIsRoundedUp) { - // Map and touch two pages, then try to mincore the first page + the first - // half of the second page. mincore should silently round up the length to - // include both pages. - constexpr size_t kTestPageCount = 2; - auto const kTestMappingBytes = kTestPageCount * kPageSize; - auto m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kTestMappingBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - memset(m.ptr(), 0, m.len()); - - std::vector<unsigned char> vec(kTestPageCount, 0); - ASSERT_THAT(mincore(m.ptr(), kPageSize + kPageSize / 2, vec.data()), - SyscallSucceeds()); - EXPECT_EQ(kTestPageCount, CountSetLSBs(vec)); -} - -TEST(MincoreTest, ZeroLengthSucceedsAndAllowsAnyVecBelowTaskSize) { - EXPECT_THAT(mincore(nullptr, 0, nullptr), SyscallSucceeds()); -} - -TEST(MincoreTest, InvalidLengthFails) { - EXPECT_THAT(mincore(nullptr, -1, nullptr), SyscallFailsWithErrno(ENOMEM)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mkdir.cc b/test/syscalls/linux/mkdir.cc deleted file mode 100644 index 11fbfa5c5..000000000 --- a/test/syscalls/linux/mkdir.cc +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.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/temp_umask.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class MkdirTest : public ::testing::Test { - protected: - // SetUp creates various configurations of files. - void SetUp() override { dirname_ = NewTempAbsPath(); } - - // TearDown unlinks created files. - void TearDown() override { - EXPECT_THAT(rmdir(dirname_.c_str()), SyscallSucceeds()); - } - - std::string dirname_; -}; - -TEST_F(MkdirTest, CanCreateWritableDir) { - ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); - std::string filename = JoinPath(dirname_, "anything"); - int fd; - ASSERT_THAT(fd = open(filename.c_str(), O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - ASSERT_THAT(unlink(filename.c_str()), SyscallSucceeds()); -} - -TEST_F(MkdirTest, HonorsUmask) { - constexpr mode_t kMask = 0111; - TempUmask mask(kMask); - ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); - struct stat statbuf; - ASSERT_THAT(stat(dirname_.c_str(), &statbuf), SyscallSucceeds()); - EXPECT_EQ(0777 & ~kMask, statbuf.st_mode & 0777); -} - -TEST_F(MkdirTest, HonorsUmask2) { - constexpr mode_t kMask = 0142; - TempUmask mask(kMask); - ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); - struct stat statbuf; - ASSERT_THAT(stat(dirname_.c_str(), &statbuf), SyscallSucceeds()); - EXPECT_EQ(0777 & ~kMask, statbuf.st_mode & 0777); -} - -TEST_F(MkdirTest, FailsOnDirWithoutWritePerms) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - ASSERT_THAT(mkdir(dirname_.c_str(), 0555), SyscallSucceeds()); - auto dir = JoinPath(dirname_.c_str(), "foo"); - EXPECT_THAT(mkdir(dir.c_str(), 0777), SyscallFailsWithErrno(EACCES)); - EXPECT_THAT(open(JoinPath(dirname_, "file").c_str(), O_RDWR | O_CREAT, 0666), - SyscallFailsWithErrno(EACCES)); -} - -TEST_F(MkdirTest, DirAlreadyExists) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); - auto dir = JoinPath(dirname_.c_str(), "foo"); - EXPECT_THAT(mkdir(dir.c_str(), 0777), SyscallSucceeds()); - - struct { - int mode; - int err; - } tests[] = { - {.mode = 0000, .err = EACCES}, // No perm - {.mode = 0100, .err = EEXIST}, // Exec only - {.mode = 0200, .err = EACCES}, // Write only - {.mode = 0300, .err = EEXIST}, // Write+exec - {.mode = 0400, .err = EACCES}, // Read only - {.mode = 0500, .err = EEXIST}, // Read+exec - {.mode = 0600, .err = EACCES}, // Read+write - {.mode = 0700, .err = EEXIST}, // All - }; - for (const auto& t : tests) { - printf("mode: 0%o\n", t.mode); - EXPECT_THAT(chmod(dirname_.c_str(), t.mode), SyscallSucceeds()); - EXPECT_THAT(mkdir(dir.c_str(), 0777), SyscallFailsWithErrno(t.err)); - } - - // Clean up. - EXPECT_THAT(chmod(dirname_.c_str(), 0777), SyscallSucceeds()); - ASSERT_THAT(rmdir(dir.c_str()), SyscallSucceeds()); -} - -TEST_F(MkdirTest, MkdirAtEmptyPath) { - ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dirname_, O_RDONLY | O_DIRECTORY, 0666)); - EXPECT_THAT(mkdirat(fd.get(), "", 0777), SyscallFailsWithErrno(ENOENT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mknod.cc b/test/syscalls/linux/mknod.cc deleted file mode 100644 index 1635c6d0c..000000000 --- a/test/syscalls/linux/mknod.cc +++ /dev/null @@ -1,234 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(MknodTest, RegularFile) { - const std::string node0 = NewTempAbsPath(); - EXPECT_THAT(mknod(node0.c_str(), S_IFREG, 0), SyscallSucceeds()); - - const std::string node1 = NewTempAbsPath(); - EXPECT_THAT(mknod(node1.c_str(), 0, 0), SyscallSucceeds()); -} - -TEST(MknodTest, RegularFilePermissions) { - const std::string node = NewTempAbsPath(); - mode_t newUmask = 0077; - umask(newUmask); - - // Attempt to open file with mode 0777. Not specifying file type should create - // a regualar file. - mode_t perms = S_IRWXU | S_IRWXG | S_IRWXO; - EXPECT_THAT(mknod(node.c_str(), perms, 0), SyscallSucceeds()); - - // In the absence of a default ACL, the permissions of the created node are - // (mode & ~umask). -- mknod(2) - mode_t wantPerms = perms & ~newUmask; - struct stat st; - ASSERT_THAT(stat(node.c_str(), &st), SyscallSucceeds()); - ASSERT_EQ(st.st_mode & 0777, wantPerms); - - // "Zero file type is equivalent to type S_IFREG." - mknod(2) - ASSERT_EQ(st.st_mode & S_IFMT, S_IFREG); -} - -TEST(MknodTest, MknodAtFIFO) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string fifo_relpath = NewTempRelPath(); - const std::string fifo = JoinPath(dir.path(), fifo_relpath); - - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path().c_str(), O_RDONLY)); - ASSERT_THAT(mknodat(dirfd.get(), fifo_relpath.c_str(), S_IFIFO | S_IRUSR, 0), - SyscallSucceeds()); - - struct stat st; - ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); - EXPECT_TRUE(S_ISFIFO(st.st_mode)); -} - -TEST(MknodTest, MknodOnExistingPathFails) { - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const TempPath slink = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), file.path())); - - EXPECT_THAT(mknod(file.path().c_str(), S_IFREG, 0), - SyscallFailsWithErrno(EEXIST)); - EXPECT_THAT(mknod(file.path().c_str(), S_IFIFO, 0), - SyscallFailsWithErrno(EEXIST)); - EXPECT_THAT(mknod(slink.path().c_str(), S_IFREG, 0), - SyscallFailsWithErrno(EEXIST)); - EXPECT_THAT(mknod(slink.path().c_str(), S_IFIFO, 0), - SyscallFailsWithErrno(EEXIST)); -} - -TEST(MknodTest, UnimplementedTypesReturnError) { - // TODO(gvisor.dev/issue/1624): These file types are supported by some - // filesystems in VFS2, so this test should be deleted along with VFS1. - SKIP_IF(!IsRunningWithVFS1()); - - const std::string path = NewTempAbsPath(); - EXPECT_THAT(mknod(path.c_str(), S_IFSOCK, 0), - SyscallFailsWithErrno(EOPNOTSUPP)); - EXPECT_THAT(mknod(path.c_str(), S_IFCHR, 0), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(mknod(path.c_str(), S_IFBLK, 0), SyscallFailsWithErrno(EPERM)); -} - -TEST(MknodTest, Socket) { - SKIP_IF(IsRunningOnGvisor() && IsRunningWithVFS1()); - - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - - auto filename = NewTempRelPath(); - - ASSERT_THAT(mknod(filename.c_str(), S_IFSOCK | S_IRUSR | S_IWUSR, 0), - SyscallSucceeds()); - - int sk; - ASSERT_THAT(sk = socket(AF_UNIX, SOCK_SEQPACKET, 0), SyscallSucceeds()); - FileDescriptor fd(sk); - - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - absl::SNPrintF(addr.sun_path, sizeof(addr.sun_path), "%s", filename.c_str()); - ASSERT_THAT(connect(sk, (struct sockaddr *)&addr, sizeof(addr)), - SyscallFailsWithErrno(ECONNREFUSED)); - ASSERT_THAT(unlink(filename.c_str()), SyscallSucceeds()); -} - -PosixErrorOr<FileDescriptor> OpenRetryEINTR(std::string const& path, int flags, - mode_t mode = 0) { - while (true) { - auto maybe_fd = Open(path, flags, mode); - if (maybe_fd.ok() || maybe_fd.error().errno_value() != EINTR) { - return maybe_fd; - } - } -} - -TEST(MknodTest, Fifo) { - const std::string fifo = NewTempAbsPath(); - ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), - SyscallSucceeds()); - - struct stat st; - ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); - EXPECT_TRUE(S_ISFIFO(st.st_mode)); - - std::string msg = "some std::string"; - std::vector<char> buf(512); - - // Read-end of the pipe. - ScopedThread t([&fifo, &buf, &msg]() { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_RDONLY)); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(msg.length())); - EXPECT_EQ(msg, std::string(buf.data())); - }); - - // Write-end of the pipe. - FileDescriptor wfd = - ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_WRONLY)); - EXPECT_THAT(WriteFd(wfd.get(), msg.c_str(), msg.length()), - SyscallSucceedsWithValue(msg.length())); -} - -TEST(MknodTest, FifoOtrunc) { - const std::string fifo = NewTempAbsPath(); - ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), - SyscallSucceeds()); - - struct stat st = {}; - ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); - EXPECT_TRUE(S_ISFIFO(st.st_mode)); - - std::string msg = "some std::string"; - std::vector<char> buf(512); - // Read-end of the pipe. - ScopedThread t([&fifo, &buf, &msg]() { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_RDONLY)); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(msg.length())); - EXPECT_EQ(msg, std::string(buf.data())); - }); - - // Write-end of the pipe. - FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE( - OpenRetryEINTR(fifo.c_str(), O_WRONLY | O_TRUNC)); - EXPECT_THAT(WriteFd(wfd.get(), msg.c_str(), msg.length()), - SyscallSucceedsWithValue(msg.length())); -} - -TEST(MknodTest, FifoTruncNoOp) { - const std::string fifo = NewTempAbsPath(); - ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), - SyscallSucceeds()); - - EXPECT_THAT(truncate(fifo.c_str(), 0), SyscallFailsWithErrno(EINVAL)); - - struct stat st = {}; - ASSERT_THAT(stat(fifo.c_str(), &st), SyscallSucceeds()); - EXPECT_TRUE(S_ISFIFO(st.st_mode)); - - std::string msg = "some std::string"; - std::vector<char> buf(512); - // Read-end of the pipe. - ScopedThread t([&fifo, &buf, &msg]() { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_RDONLY)); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(msg.length())); - EXPECT_EQ(msg, std::string(buf.data())); - }); - - FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE( - OpenRetryEINTR(fifo.c_str(), O_WRONLY | O_TRUNC)); - EXPECT_THAT(ftruncate(wfd.get(), 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(WriteFd(wfd.get(), msg.c_str(), msg.length()), - SyscallSucceedsWithValue(msg.length())); - EXPECT_THAT(ftruncate(wfd.get(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(MknodTest, MknodAtEmptyPath) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY, 0666)); - EXPECT_THAT(mknodat(fd.get(), "", S_IFREG | 0777, 0), - SyscallFailsWithErrno(ENOENT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mlock.cc b/test/syscalls/linux/mlock.cc deleted file mode 100644 index 78ac96bed..000000000 --- a/test/syscalls/linux/mlock.cc +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include <cerrno> -#include <cstring> - -#include "gmock/gmock.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/rlimit_util.h" -#include "test/util/test_util.h" - -using ::testing::_; - -namespace gvisor { -namespace testing { - -namespace { - -PosixErrorOr<bool> CanMlock() { - struct rlimit rlim; - if (getrlimit(RLIMIT_MEMLOCK, &rlim) < 0) { - return PosixError(errno, "getrlimit(RLIMIT_MEMLOCK)"); - } - if (rlim.rlim_cur != 0) { - return true; - } - return HaveCapability(CAP_IPC_LOCK); -} - -// Returns true if the page containing addr is mlocked. -bool IsPageMlocked(uintptr_t addr) { - // This relies on msync(MS_INVALIDATE) interacting correctly with mlocked - // pages, which is tested for by the MsyncInvalidate case below. - int const rv = msync(reinterpret_cast<void*>(addr & ~(kPageSize - 1)), - kPageSize, MS_ASYNC | MS_INVALIDATE); - if (rv == 0) { - return false; - } - // This uses TEST_PCHECK_MSG since it's used in subprocesses. - TEST_PCHECK_MSG(errno == EBUSY, "msync failed with unexpected errno"); - return true; -} - -TEST(MlockTest, Basic) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); -} - -TEST(MlockTest, ProtNone) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), - SyscallFailsWithErrno(ENOMEM)); - // ENOMEM is returned because mlock can't populate the page, but it's still - // considered locked. - EXPECT_TRUE(IsPageMlocked(mapping.addr())); -} - -TEST(MlockTest, MadviseDontneed) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_THAT(madvise(mapping.ptr(), mapping.len(), MADV_DONTNEED), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(MlockTest, MsyncInvalidate) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_THAT(msync(mapping.ptr(), mapping.len(), MS_ASYNC | MS_INVALIDATE), - SyscallFailsWithErrno(EBUSY)); - EXPECT_THAT(msync(mapping.ptr(), mapping.len(), MS_SYNC | MS_INVALIDATE), - SyscallFailsWithErrno(EBUSY)); -} - -TEST(MlockTest, Fork) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - EXPECT_THAT( - InForkedProcess([&] { TEST_CHECK(!IsPageMlocked(mapping.addr())); }), - IsPosixErrorOkAndHolds(0)); -} - -TEST(MlockTest, RlimitMemlockZero) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } - Cleanup reset_rlimit = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, 0)); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), - SyscallFailsWithErrno(EPERM)); -} - -TEST(MlockTest, RlimitMemlockInsufficient) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } - Cleanup reset_rlimit = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, kPageSize)); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), - SyscallFailsWithErrno(ENOMEM)); -} - -TEST(MunlockTest, Basic) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(munlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); -} - -TEST(MunlockTest, NotLocked) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - EXPECT_THAT(munlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); -} - -// There is currently no test for mlockall(MCL_CURRENT) because the default -// RLIMIT_MEMLOCK of 64 KB is insufficient to actually invoke -// mlockall(MCL_CURRENT). - -TEST(MlockallTest, Future) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - - // Run this test in a separate (single-threaded) subprocess to ensure that a - // background thread doesn't try to mmap a large amount of memory, fail due - // to hitting RLIMIT_MEMLOCK, and explode the process violently. - auto const do_test = [] { - auto const mapping = - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE).ValueOrDie(); - TEST_CHECK(!IsPageMlocked(mapping.addr())); - TEST_PCHECK(mlockall(MCL_FUTURE) == 0); - // Ensure that mlockall(MCL_FUTURE) is turned off before the end of the - // test, as otherwise mmaps may fail unexpectedly. - Cleanup do_munlockall([] { TEST_PCHECK(munlockall() == 0); }); - auto const mapping2 = - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE).ValueOrDie(); - TEST_CHECK(IsPageMlocked(mapping2.addr())); - // Fire munlockall() and check that it disables mlockall(MCL_FUTURE). - do_munlockall.Release()(); - auto const mapping3 = - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE).ValueOrDie(); - TEST_CHECK(!IsPageMlocked(mapping2.addr())); - }; - EXPECT_THAT(InForkedProcess(do_test), IsPosixErrorOkAndHolds(0)); -} - -TEST(MunlockallTest, Basic) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(munlockall(), SyscallSucceeds()); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); -} - -#ifndef SYS_mlock2 -#if defined(__x86_64__) -#define SYS_mlock2 325 -#elif defined(__aarch64__) -#define SYS_mlock2 284 -#endif -#endif - -#ifndef MLOCK_ONFAULT -#define MLOCK_ONFAULT 0x01 // Linux: include/uapi/asm-generic/mman-common.h -#endif - -#ifdef SYS_mlock2 - -int mlock2(void const* addr, size_t len, int flags) { - return syscall(SYS_mlock2, addr, len, flags); -} - -TEST(Mlock2Test, NoFlags) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock2(mapping.ptr(), mapping.len(), 0), SyscallSucceeds()); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); -} - -TEST(Mlock2Test, MlockOnfault) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); - ASSERT_THAT(mlock2(mapping.ptr(), mapping.len(), MLOCK_ONFAULT), - SyscallSucceeds()); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); -} - -TEST(Mlock2Test, UnknownFlags) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - EXPECT_THAT(mlock2(mapping.ptr(), mapping.len(), ~0), - SyscallFailsWithErrno(EINVAL)); -} - -#endif // defined(SYS_mlock2) - -TEST(MapLockedTest, Basic) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - EXPECT_THAT(munlock(mapping.ptr(), mapping.len()), SyscallSucceeds()); - EXPECT_FALSE(IsPageMlocked(mapping.addr())); -} - -TEST(MapLockedTest, RlimitMemlockZero) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } - Cleanup reset_rlimit = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, 0)); - EXPECT_THAT( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED), - PosixErrorIs(EPERM, _)); -} - -TEST(MapLockedTest, RlimitMemlockInsufficient) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } - Cleanup reset_rlimit = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, kPageSize)); - EXPECT_THAT( - MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED), - PosixErrorIs(EAGAIN, _)); -} - -TEST(MremapLockedTest, Basic) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - - void* addr = mremap(mapping.ptr(), mapping.len(), 2 * mapping.len(), - MREMAP_MAYMOVE, nullptr); - if (addr == MAP_FAILED) { - FAIL() << "mremap failed: " << errno << " (" << strerror(errno) << ")"; - } - mapping.release(); - mapping.reset(addr, 2 * mapping.len()); - EXPECT_TRUE(IsPageMlocked(reinterpret_cast<uintptr_t>(addr))); -} - -TEST(MremapLockedTest, RlimitMemlockZero) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } - Cleanup reset_rlimit = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, 0)); - void* addr = mremap(mapping.ptr(), mapping.len(), 2 * mapping.len(), - MREMAP_MAYMOVE, nullptr); - EXPECT_TRUE(addr == MAP_FAILED && errno == EAGAIN) - << "addr = " << addr << ", errno = " << errno; -} - -TEST(MremapLockedTest, RlimitMemlockInsufficient) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock())); - auto mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); - EXPECT_TRUE(IsPageMlocked(mapping.addr())); - - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } - Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE( - ScopedSetSoftRlimit(RLIMIT_MEMLOCK, mapping.len())); - void* addr = mremap(mapping.ptr(), mapping.len(), 2 * mapping.len(), - MREMAP_MAYMOVE, nullptr); - EXPECT_TRUE(addr == MAP_FAILED && errno == EAGAIN) - << "addr = " << addr << ", errno = " << errno; -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc deleted file mode 100644 index 93a6d9cde..000000000 --- a/test/syscalls/linux/mmap.cc +++ /dev/null @@ -1,1697 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <linux/magic.h> -#include <linux/unistd.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/statfs.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/escaping.h" -#include "absl/strings/str_split.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::AnyOf; -using ::testing::Eq; -using ::testing::Gt; - -namespace gvisor { -namespace testing { - -namespace { - -PosixErrorOr<int64_t> VirtualMemorySize() { - ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/statm")); - std::vector<std::string> parts = absl::StrSplit(contents, ' '); - if (parts.empty()) { - return PosixError(EINVAL, "Unable to parse /proc/self/statm"); - } - ASSIGN_OR_RETURN_ERRNO(auto pages, Atoi<int64_t>(parts[0])); - return pages * getpagesize(); -} - -class MMapTest : public ::testing::Test { - protected: - // Unmap mapping, if one was made. - void TearDown() override { - if (addr_) { - EXPECT_THAT(Unmap(), SyscallSucceeds()); - } - } - - // Remembers mapping, so it can be automatically unmapped. - uintptr_t Map(uintptr_t addr, size_t length, int prot, int flags, int fd, - off_t offset) { - void* ret = - mmap(reinterpret_cast<void*>(addr), length, prot, flags, fd, offset); - - if (ret != MAP_FAILED) { - addr_ = ret; - length_ = length; - } - - return reinterpret_cast<uintptr_t>(ret); - } - - // Unmap previous mapping - int Unmap() { - if (!addr_) { - return -1; - } - - int ret = munmap(addr_, length_); - - addr_ = nullptr; - length_ = 0; - - return ret; - } - - // Msync the mapping. - int Msync() { return msync(addr_, length_, MS_SYNC); } - - // Mlock the mapping. - int Mlock() { return mlock(addr_, length_); } - - // Munlock the mapping. - int Munlock() { return munlock(addr_, length_); } - - int Protect(uintptr_t addr, size_t length, int prot) { - return mprotect(reinterpret_cast<void*>(addr), length, prot); - } - - void* addr_ = nullptr; - size_t length_ = 0; -}; - -// Matches if arg contains the same contents as string str. -MATCHER_P(EqualsMemory, str, "") { - if (0 == memcmp(arg, str.c_str(), str.size())) { - return true; - } - - *result_listener << "Memory did not match. Got:\n" - << absl::BytesToHexString( - std::string(static_cast<char*>(arg), str.size())) - << "Want:\n" - << absl::BytesToHexString(str); - return false; -} - -// We can't map pipes, but for different reasons. -TEST_F(MMapTest, MapPipe) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fds[0], 0), - SyscallFailsWithErrno(ENODEV)); - EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fds[1], 0), - SyscallFailsWithErrno(EACCES)); - ASSERT_THAT(close(fds[0]), SyscallSucceeds()); - ASSERT_THAT(close(fds[1]), SyscallSucceeds()); -} - -// It's very common to mmap /dev/zero because anonymous mappings aren't part -// of POSIX although they are widely supported. So a zero initialized memory -// region would actually come from a "file backed" /dev/zero mapping. -TEST_F(MMapTest, MapDevZeroShared) { - // This test will verify that we're able to map a page backed by /dev/zero - // as MAP_SHARED. - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - - // Test that we can create a RW SHARED mapping of /dev/zero. - ASSERT_THAT( - Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0), - SyscallSucceeds()); -} - -TEST_F(MMapTest, MapDevZeroPrivate) { - // This test will verify that we're able to map a page backed by /dev/zero - // as MAP_PRIVATE. - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - - // Test that we can create a RW SHARED mapping of /dev/zero. - ASSERT_THAT( - Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero.get(), 0), - SyscallSucceeds()); -} - -TEST_F(MMapTest, MapDevZeroNoPersistence) { - // This test will verify that two independent mappings of /dev/zero do not - // appear to reference the same "backed file." - - const FileDescriptor dev_zero1 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - const FileDescriptor dev_zero2 = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - - ASSERT_THAT( - Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero1.get(), 0), - SyscallSucceeds()); - - // Create a second mapping via the second /dev/zero fd. - void* psec_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - dev_zero2.get(), 0); - ASSERT_THAT(reinterpret_cast<intptr_t>(psec_map), SyscallSucceeds()); - - // Always unmap. - auto cleanup_psec_map = Cleanup( - [&] { EXPECT_THAT(munmap(psec_map, kPageSize), SyscallSucceeds()); }); - - // Verify that we have independently addressed pages. - ASSERT_NE(psec_map, addr_); - - std::string buf_zero(kPageSize, 0x00); - std::string buf_ones(kPageSize, 0xFF); - - // Verify the first is actually all zeros after mmap. - EXPECT_THAT(addr_, EqualsMemory(buf_zero)); - - // Let's fill in the first mapping with 0xFF. - memcpy(addr_, buf_ones.data(), kPageSize); - - // Verify that the memcpy actually stuck in the page. - EXPECT_THAT(addr_, EqualsMemory(buf_ones)); - - // Verify that it didn't affect the second page which should be all zeros. - EXPECT_THAT(psec_map, EqualsMemory(buf_zero)); -} - -TEST_F(MMapTest, MapDevZeroSharedMultiplePages) { - // This will test that we're able to map /dev/zero over multiple pages. - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - - // Test that we can create a RW SHARED mapping of /dev/zero. - ASSERT_THAT(Map(0, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, - dev_zero.get(), 0), - SyscallSucceeds()); - - std::string buf_zero(kPageSize * 2, 0x00); - std::string buf_ones(kPageSize * 2, 0xFF); - - // Verify the two pages are actually all zeros after mmap. - EXPECT_THAT(addr_, EqualsMemory(buf_zero)); - - // Fill out the pages with all ones. - memcpy(addr_, buf_ones.data(), kPageSize * 2); - - // Verify that the memcpy actually stuck in the pages. - EXPECT_THAT(addr_, EqualsMemory(buf_ones)); -} - -TEST_F(MMapTest, MapDevZeroSharedFdNoPersistence) { - // This test will verify that two independent mappings of /dev/zero do not - // appear to reference the same "backed file" even when mapped from the - // same initial fd. - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - - ASSERT_THAT( - Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0), - SyscallSucceeds()); - - // Create a second mapping via the same fd. - void* psec_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - dev_zero.get(), 0); - ASSERT_THAT(reinterpret_cast<int64_t>(psec_map), SyscallSucceeds()); - - // Always unmap. - auto cleanup_psec_map = Cleanup( - [&] { ASSERT_THAT(munmap(psec_map, kPageSize), SyscallSucceeds()); }); - - // Verify that we have independently addressed pages. - ASSERT_NE(psec_map, addr_); - - std::string buf_zero(kPageSize, 0x00); - std::string buf_ones(kPageSize, 0xFF); - - // Verify the first is actually all zeros after mmap. - EXPECT_THAT(addr_, EqualsMemory(buf_zero)); - - // Let's fill in the first mapping with 0xFF. - memcpy(addr_, buf_ones.data(), kPageSize); - - // Verify that the memcpy actually stuck in the page. - EXPECT_THAT(addr_, EqualsMemory(buf_ones)); - - // Verify that it didn't affect the second page which should be all zeros. - EXPECT_THAT(psec_map, EqualsMemory(buf_zero)); -} - -TEST_F(MMapTest, MapDevZeroSegfaultAfterUnmap) { - SetupGvisorDeathTest(); - - // This test will verify that we're able to map a page backed by /dev/zero - // as MAP_SHARED and after it's unmapped any access results in a SIGSEGV. - // This test is redundant but given the special nature of /dev/zero mappings - // it doesn't hurt. - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - - const auto rest = [&] { - // Test that we can create a RW SHARED mapping of /dev/zero. - TEST_PCHECK(Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - dev_zero.get(), - 0) != reinterpret_cast<uintptr_t>(MAP_FAILED)); - - // Confirm that accesses after the unmap result in a SIGSEGV. - // - // N.B. We depend on this process being single-threaded to ensure there - // can't be another mmap to map addr before the dereference below. - void* addr_saved = addr_; // Unmap resets addr_. - TEST_PCHECK(Unmap() == 0); - *reinterpret_cast<volatile int*>(addr_saved) = 0xFF; - }; - - EXPECT_THAT(InForkedProcess(rest), - IsPosixErrorOkAndHolds(AnyOf(Eq(W_EXITCODE(0, SIGSEGV)), - Eq(W_EXITCODE(0, 128 + SIGSEGV))))); -} - -TEST_F(MMapTest, MapDevZeroUnaligned) { - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR)); - const size_t size = kPageSize + kPageSize / 2; - const std::string buf_zero(size, 0x00); - - ASSERT_THAT( - Map(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0), - SyscallSucceeds()); - EXPECT_THAT(addr_, EqualsMemory(buf_zero)); - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - ASSERT_THAT( - Map(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero.get(), 0), - SyscallSucceeds()); - EXPECT_THAT(addr_, EqualsMemory(buf_zero)); -} - -// We can't map _some_ character devices. -TEST_F(MMapTest, MapCharDevice) { - const FileDescriptor cdevfd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/random", 0, 0)); - EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, cdevfd.get(), 0), - SyscallFailsWithErrno(ENODEV)); -} - -// We can't map directories. -TEST_F(MMapTest, MapDirectory) { - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), 0, 0)); - EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, dirfd.get(), 0), - SyscallFailsWithErrno(ENODEV)); -} - -// We can map *something* -TEST_F(MMapTest, MapAnything) { - EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceedsWithValue(Gt(0))); -} - -// Map length < PageSize allowed -TEST_F(MMapTest, SmallMap) { - EXPECT_THAT(Map(0, 128, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); -} - -// Hint address doesn't break anything. -// Note: there is no requirement we actually get the hint address -TEST_F(MMapTest, HintAddress) { - EXPECT_THAT( - Map(0x30000000, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); -} - -// MAP_FIXED gives us exactly the requested address -TEST_F(MMapTest, MapFixed) { - EXPECT_THAT(Map(0x30000000, kPageSize, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0), - SyscallSucceedsWithValue(0x30000000)); -} - -// 64-bit addresses work too -#if defined(__x86_64__) || defined(__aarch64__) -TEST_F(MMapTest, MapFixed64) { - EXPECT_THAT(Map(0x300000000000, kPageSize, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0), - SyscallSucceedsWithValue(0x300000000000)); -} -#endif - -// MAP_STACK allowed. -// There isn't a good way to verify it did anything. -TEST_F(MMapTest, MapStack) { - EXPECT_THAT(Map(0, kPageSize, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0), - SyscallSucceeds()); -} - -// MAP_LOCKED allowed. -// There isn't a good way to verify it did anything. -TEST_F(MMapTest, MapLocked) { - EXPECT_THAT(Map(0, kPageSize, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0), - SyscallSucceeds()); -} - -// MAP_PRIVATE or MAP_SHARED must be passed -TEST_F(MMapTest, NotPrivateOrShared) { - EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_ANONYMOUS, -1, 0), - SyscallFailsWithErrno(EINVAL)); -} - -// Only one of MAP_PRIVATE or MAP_SHARED may be passed -TEST_F(MMapTest, PrivateAndShared) { - EXPECT_THAT(Map(0, kPageSize, PROT_NONE, - MAP_PRIVATE | MAP_SHARED | MAP_ANONYMOUS, -1, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(MMapTest, FixedAlignment) { - // Addr must be page aligned (MAP_FIXED) - EXPECT_THAT(Map(0x30000001, kPageSize, PROT_NONE, - MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0), - SyscallFailsWithErrno(EINVAL)); -} - -// Non-MAP_FIXED address does not need to be page aligned -TEST_F(MMapTest, NonFixedAlignment) { - EXPECT_THAT( - Map(0x30000001, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); -} - -// Length = 0 results in EINVAL. -TEST_F(MMapTest, InvalidLength) { - EXPECT_THAT(Map(0, 0, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallFailsWithErrno(EINVAL)); -} - -// Bad fd not allowed. -TEST_F(MMapTest, BadFd) { - EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_PRIVATE, 999, 0), - SyscallFailsWithErrno(EBADF)); -} - -// Mappings are writable. -TEST_F(MMapTest, ProtWrite) { - uint64_t addr; - constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; - - EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - // This shouldn't cause a SIGSEGV. - memset(reinterpret_cast<void*>(addr), 42, kPageSize); - - // The written data should actually be there. - EXPECT_EQ( - 0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord))); -} - -// "Write-only" mappings are writable *and* readable. -TEST_F(MMapTest, ProtWriteOnly) { - uint64_t addr; - constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; - - EXPECT_THAT( - addr = Map(0, kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - // This shouldn't cause a SIGSEGV. - memset(reinterpret_cast<void*>(addr), 42, kPageSize); - - // The written data should actually be there. - EXPECT_EQ( - 0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord))); -} - -// "Write-only" mappings are readable. -// -// This is distinct from above to ensure the page is accessible even if the -// initial fault is a write fault. -TEST_F(MMapTest, ProtWriteOnlyReadable) { - uint64_t addr; - constexpr uint64_t kFirstWord = 0; - - EXPECT_THAT( - addr = Map(0, kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), &kFirstWord, - sizeof(kFirstWord))); -} - -// Mappings are writable after mprotect from PROT_NONE to PROT_READ|PROT_WRITE. -TEST_F(MMapTest, ProtectProtWrite) { - uint64_t addr; - constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; - - EXPECT_THAT( - addr = Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_WRITE), - SyscallSucceeds()); - - // This shouldn't cause a SIGSEGV. - memset(reinterpret_cast<void*>(addr), 42, kPageSize); - - // The written data should actually be there. - EXPECT_EQ( - 0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord))); -} - -// SIGSEGV raised when reading PROT_NONE memory -TEST_F(MMapTest, ProtNoneDeath) { - SetupGvisorDeathTest(); - - uintptr_t addr; - - ASSERT_THAT( - addr = Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - EXPECT_EXIT(*reinterpret_cast<volatile int*>(addr), - ::testing::KilledBySignal(SIGSEGV), ""); -} - -// SIGSEGV raised when writing PROT_READ only memory -TEST_F(MMapTest, ReadOnlyDeath) { - SetupGvisorDeathTest(); - - uintptr_t addr; - - ASSERT_THAT( - addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - EXPECT_EXIT(*reinterpret_cast<volatile int*>(addr) = 42, - ::testing::KilledBySignal(SIGSEGV), ""); -} - -// Writable mapping mprotect'd to read-only should not be writable. -TEST_F(MMapTest, MprotectReadOnlyDeath) { - SetupGvisorDeathTest(); - - uintptr_t addr; - - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - volatile int* val = reinterpret_cast<int*>(addr); - - // Copy to ensure page is mapped in. - *val = 42; - - ASSERT_THAT(Protect(addr, kPageSize, PROT_READ), SyscallSucceeds()); - - // Now it shouldn't be writable. - EXPECT_EXIT(*val = 0, ::testing::KilledBySignal(SIGSEGV), ""); -} - -// Verify that calling mprotect an address that's not page aligned fails. -TEST_F(MMapTest, MprotectNotPageAligned) { - uintptr_t addr; - - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - ASSERT_THAT(Protect(addr + 1, kPageSize - 1, PROT_READ), - SyscallFailsWithErrno(EINVAL)); -} - -// Verify that calling mprotect with an absurdly huge length fails. -TEST_F(MMapTest, MprotectHugeLength) { - uintptr_t addr; - - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - ASSERT_THAT(Protect(addr, static_cast<size_t>(-1), PROT_READ), - SyscallFailsWithErrno(ENOMEM)); -} - -#if defined(__x86_64__) || defined(__i386__) -// This code is equivalent in 32 and 64-bit mode -const uint8_t machine_code[] = { - 0xb8, 0x2a, 0x00, 0x00, 0x00, // movl $42, %eax - 0xc3, // retq -}; -#elif defined(__aarch64__) -const uint8_t machine_code[] = { - 0x40, 0x05, 0x80, 0x52, // mov w0, #42 - 0xc0, 0x03, 0x5f, 0xd6, // ret -}; -#endif - -// PROT_EXEC allows code execution -TEST_F(MMapTest, ProtExec) { - uintptr_t addr; - uint32_t (*func)(void); - - EXPECT_THAT(addr = Map(0, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - memcpy(reinterpret_cast<void*>(addr), machine_code, sizeof(machine_code)); - -#if defined(__aarch64__) - // We use this as a memory barrier for Arm64. - ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_EXEC), - SyscallSucceeds()); -#endif - - func = reinterpret_cast<uint32_t (*)(void)>(addr); - - EXPECT_EQ(42, func()); -} - -// No PROT_EXEC disallows code execution -TEST_F(MMapTest, NoProtExecDeath) { - SetupGvisorDeathTest(); - - uintptr_t addr; - uint32_t (*func)(void); - - EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - - memcpy(reinterpret_cast<void*>(addr), machine_code, sizeof(machine_code)); - - func = reinterpret_cast<uint32_t (*)(void)>(addr); - - EXPECT_EXIT(func(), ::testing::KilledBySignal(SIGSEGV), ""); -} - -TEST_F(MMapTest, NoExceedLimitData) { - void* prevbrk; - void* target_brk; - struct rlimit setlim; - - prevbrk = sbrk(0); - ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk)); - target_brk = reinterpret_cast<char*>(prevbrk) + 1; - - setlim.rlim_cur = RLIM_INFINITY; - setlim.rlim_max = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); - EXPECT_THAT(brk(target_brk), SyscallSucceedsWithValue(0)); -} - -TEST_F(MMapTest, ExceedLimitData) { - // To unit test this more precisely, we'd need access to the mm's start_brk - // and end_brk, which we don't have direct access to :/ - void* prevbrk; - void* target_brk; - struct rlimit setlim; - - prevbrk = sbrk(0); - ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk)); - target_brk = reinterpret_cast<char*>(prevbrk) + 8192; - - setlim.rlim_cur = 0; - setlim.rlim_max = RLIM_INFINITY; - // Set RLIMIT_DATA very low so any subsequent brk() calls fail. - // Reset RLIMIT_DATA during teardown step. - ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); - EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM)); - // Teardown step... - setlim.rlim_cur = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); -} - -TEST_F(MMapTest, ExceedLimitDataPrlimit) { - // To unit test this more precisely, we'd need access to the mm's start_brk - // and end_brk, which we don't have direct access to :/ - void* prevbrk; - void* target_brk; - struct rlimit setlim; - - prevbrk = sbrk(0); - ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk)); - target_brk = reinterpret_cast<char*>(prevbrk) + 8192; - - setlim.rlim_cur = 0; - setlim.rlim_max = RLIM_INFINITY; - // Set RLIMIT_DATA very low so any subsequent brk() calls fail. - // Reset RLIMIT_DATA during teardown step. - ASSERT_THAT(prlimit(0, RLIMIT_DATA, &setlim, nullptr), SyscallSucceeds()); - EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM)); - // Teardown step... - setlim.rlim_cur = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); -} - -TEST_F(MMapTest, ExceedLimitDataPrlimitPID) { - // To unit test this more precisely, we'd need access to the mm's start_brk - // and end_brk, which we don't have direct access to :/ - void* prevbrk; - void* target_brk; - struct rlimit setlim; - - prevbrk = sbrk(0); - ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk)); - target_brk = reinterpret_cast<char*>(prevbrk) + 8192; - - setlim.rlim_cur = 0; - setlim.rlim_max = RLIM_INFINITY; - // Set RLIMIT_DATA very low so any subsequent brk() calls fail. - // Reset RLIMIT_DATA during teardown step. - ASSERT_THAT(prlimit(syscall(__NR_gettid), RLIMIT_DATA, &setlim, nullptr), - SyscallSucceeds()); - EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM)); - // Teardown step... - setlim.rlim_cur = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds()); -} - -TEST_F(MMapTest, NoExceedLimitAS) { - constexpr uint64_t kAllocBytes = 200 << 20; - // Add some headroom to the AS limit in case of e.g. unexpected stack - // expansion. - constexpr uint64_t kExtraASBytes = kAllocBytes + (20 << 20); - static_assert(kAllocBytes < kExtraASBytes, - "test depends on allocation not exceeding AS limit"); - - auto vss = ASSERT_NO_ERRNO_AND_VALUE(VirtualMemorySize()); - struct rlimit setlim; - setlim.rlim_cur = vss + kExtraASBytes; - setlim.rlim_max = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_AS, &setlim), SyscallSucceeds()); - EXPECT_THAT( - Map(0, kAllocBytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceedsWithValue(Gt(0))); -} - -TEST_F(MMapTest, ExceedLimitAS) { - constexpr uint64_t kAllocBytes = 200 << 20; - // Add some headroom to the AS limit in case of e.g. unexpected stack - // expansion. - constexpr uint64_t kExtraASBytes = 20 << 20; - static_assert(kAllocBytes > kExtraASBytes, - "test depends on allocation exceeding AS limit"); - - auto vss = ASSERT_NO_ERRNO_AND_VALUE(VirtualMemorySize()); - struct rlimit setlim; - setlim.rlim_cur = vss + kExtraASBytes; - setlim.rlim_max = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_AS, &setlim), SyscallSucceeds()); - EXPECT_THAT( - Map(0, kAllocBytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallFailsWithErrno(ENOMEM)); -} - -// Tests that setting an anonymous mmap to PROT_NONE doesn't free the memory. -TEST_F(MMapTest, SettingProtNoneDoesntFreeMemory) { - uintptr_t addr; - constexpr uint8_t kFirstWord[] = {42, 42, 42, 42}; - - EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceedsWithValue(Gt(0))); - - memset(reinterpret_cast<void*>(addr), 42, kPageSize); - - ASSERT_THAT(Protect(addr, kPageSize, PROT_NONE), SyscallSucceeds()); - ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_WRITE), - SyscallSucceeds()); - - // The written data should still be there. - EXPECT_EQ( - 0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord))); -} - -constexpr char kFileContents[] = "Hello World!"; - -class MMapFileTest : public MMapTest { - protected: - FileDescriptor fd_; - std::string filename_; - - // Open a file for read/write - void SetUp() override { - MMapTest::SetUp(); - - filename_ = NewTempAbsPath(); - fd_ = ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_CREAT | O_RDWR, 0644)); - - // Extend file so it can be written once mapped. Deliberately make the file - // only half a page in size, so we can test what happens when we access the - // second half. - // Use ftruncate(2) once the sentry supports it. - char zero = 0; - size_t count = 0; - do { - const DisableSave ds; // saving 2048 times is slow and useless. - Write(&zero, 1), SyscallSucceedsWithValue(1); - } while (++count < (kPageSize / 2)); - ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - } - - // Close and delete file - void TearDown() override { - MMapTest::TearDown(); - fd_.reset(); // Make sure the files is closed before we unlink it. - ASSERT_THAT(unlink(filename_.c_str()), SyscallSucceeds()); - } - - ssize_t Read(char* buf, size_t count) { - ssize_t len = 0; - do { - ssize_t ret = read(fd_.get(), buf, count); - if (ret < 0) { - return ret; - } else if (ret == 0) { - return len; - } - - len += ret; - buf += ret; - } while (len < static_cast<ssize_t>(count)); - - return len; - } - - ssize_t Write(const char* buf, size_t count) { - ssize_t len = 0; - do { - ssize_t ret = write(fd_.get(), buf, count); - if (ret < 0) { - return ret; - } else if (ret == 0) { - return len; - } - - len += ret; - buf += ret; - } while (len < static_cast<ssize_t>(count)); - - return len; - } -}; - -class MMapFileParamTest - : public MMapFileTest, - public ::testing::WithParamInterface<std::tuple<int, int>> { - protected: - int prot() const { return std::get<0>(GetParam()); } - - int flags() const { return std::get<1>(GetParam()); } -}; - -// MAP_POPULATE allowed. -// There isn't a good way to verify it actually did anything. -TEST_P(MMapFileParamTest, MapPopulate) { - ASSERT_THAT(Map(0, kPageSize, prot(), flags() | MAP_POPULATE, fd_.get(), 0), - SyscallSucceeds()); -} - -// MAP_POPULATE on a short file. -TEST_P(MMapFileParamTest, MapPopulateShort) { - ASSERT_THAT( - Map(0, 2 * kPageSize, prot(), flags() | MAP_POPULATE, fd_.get(), 0), - SyscallSucceeds()); -} - -// Read contents from mapped file. -TEST_F(MMapFileTest, Read) { - size_t len = strlen(kFileContents); - ASSERT_EQ(len, Write(kFileContents, len)); - - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd_.get(), 0), - SyscallSucceeds()); - - EXPECT_THAT(reinterpret_cast<char*>(addr), - EqualsMemory(std::string(kFileContents))); -} - -// Map at an offset. -TEST_F(MMapFileTest, MapOffset) { - ASSERT_THAT(lseek(fd_.get(), kPageSize, SEEK_SET), SyscallSucceeds()); - - size_t len = strlen(kFileContents); - ASSERT_EQ(len, Write(kFileContents, len)); - - uintptr_t addr; - ASSERT_THAT( - addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd_.get(), kPageSize), - SyscallSucceeds()); - - EXPECT_THAT(reinterpret_cast<char*>(addr), - EqualsMemory(std::string(kFileContents))); -} - -TEST_F(MMapFileTest, MapOffsetBeyondEnd) { - SetupGvisorDeathTest(); - - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, - fd_.get(), 10 * kPageSize), - SyscallSucceeds()); - - // Touching the memory causes SIGBUS. - size_t len = strlen(kFileContents); - EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, - reinterpret_cast<volatile char*>(addr)), - ::testing::KilledBySignal(SIGBUS), ""); -} - -// Verify mmap fails when sum of length and offset overflows. -TEST_F(MMapFileTest, MapLengthPlusOffsetOverflows) { - const size_t length = static_cast<size_t>(-kPageSize); - const off_t offset = kPageSize; - ASSERT_THAT(Map(0, length, PROT_READ, MAP_PRIVATE, fd_.get(), offset), - SyscallFailsWithErrno(ENOMEM)); -} - -// MAP_PRIVATE PROT_WRITE is allowed on read-only FDs. -TEST_F(MMapFileTest, WritePrivateOnReadOnlyFd) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY)); - - uintptr_t addr; - EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, - fd.get(), 0), - SyscallSucceeds()); - - // Touch the page to ensure the kernel didn't lie about writability. - size_t len = strlen(kFileContents); - std::copy(kFileContents, kFileContents + len, - reinterpret_cast<volatile char*>(addr)); -} - -// MAP_SHARED PROT_WRITE not allowed on read-only FDs. -TEST_F(MMapFileTest, WriteSharedOnReadOnlyFd) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY)); - - uintptr_t addr; - EXPECT_THAT( - addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0), - SyscallFailsWithErrno(EACCES)); -} - -// Mmap not allowed on O_PATH FDs. -TEST_F(MMapFileTest, MmapFileWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - uintptr_t addr; - EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd.get(), 0), - SyscallFailsWithErrno(EBADF)); -} - -// The FD must be readable. -TEST_P(MMapFileParamTest, WriteOnlyFd) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY)); - - uintptr_t addr; - EXPECT_THAT(addr = Map(0, kPageSize, prot(), flags(), fd.get(), 0), - SyscallFailsWithErrno(EACCES)); -} - -// Overwriting the contents of a file mapped MAP_SHARED PROT_READ -// should cause the new data to be reflected in the mapping. -TEST_F(MMapFileTest, ReadSharedConsistentWithOverwrite) { - // Start from scratch. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Expand the file to two pages and dirty them. - std::string bufA(kPageSize, 'a'); - ASSERT_THAT(Write(bufA.c_str(), bufA.size()), - SyscallSucceedsWithValue(bufA.size())); - std::string bufB(kPageSize, 'b'); - ASSERT_THAT(Write(bufB.c_str(), bufB.size()), - SyscallSucceedsWithValue(bufB.size())); - - // Map the page. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - // Check that the mapping contains the right file data. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufA.c_str(), kPageSize)); - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize), bufB.c_str(), - kPageSize)); - - // Start at the beginning of the file. - ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Swap the write pattern. - ASSERT_THAT(Write(bufB.c_str(), bufB.size()), - SyscallSucceedsWithValue(bufB.size())); - ASSERT_THAT(Write(bufA.c_str(), bufA.size()), - SyscallSucceedsWithValue(bufA.size())); - - // Check that the mapping got updated. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufB.c_str(), kPageSize)); - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize), bufA.c_str(), - kPageSize)); -} - -// Partially overwriting a file mapped MAP_SHARED PROT_READ should be reflected -// in the mapping. -TEST_F(MMapFileTest, ReadSharedConsistentWithPartialOverwrite) { - // Start from scratch. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Expand the file to two pages and dirty them. - std::string bufA(kPageSize, 'a'); - ASSERT_THAT(Write(bufA.c_str(), bufA.size()), - SyscallSucceedsWithValue(bufA.size())); - std::string bufB(kPageSize, 'b'); - ASSERT_THAT(Write(bufB.c_str(), bufB.size()), - SyscallSucceedsWithValue(bufB.size())); - - // Map the page. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - // Check that the mapping contains the right file data. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufA.c_str(), kPageSize)); - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize), bufB.c_str(), - kPageSize)); - - // Start at the beginning of the file. - ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Do a partial overwrite, spanning both pages. - std::string bufC(kPageSize + (kPageSize / 2), 'c'); - ASSERT_THAT(Write(bufC.c_str(), bufC.size()), - SyscallSucceedsWithValue(bufC.size())); - - // Check that the mapping got updated. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufC.c_str(), - kPageSize + (kPageSize / 2))); - EXPECT_EQ(0, - memcmp(reinterpret_cast<void*>(addr + kPageSize + (kPageSize / 2)), - bufB.c_str(), kPageSize / 2)); -} - -// Overwriting a file mapped MAP_SHARED PROT_READ should be reflected in the -// mapping and the file. -TEST_F(MMapFileTest, ReadSharedConsistentWithWriteAndFile) { - // Start from scratch. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Expand the file to two full pages and dirty it. - std::string bufA(2 * kPageSize, 'a'); - ASSERT_THAT(Write(bufA.c_str(), bufA.size()), - SyscallSucceedsWithValue(bufA.size())); - - // Map only the first page. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - // Prepare to overwrite the file contents. - ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Overwrite everything, beyond the mapped portion. - std::string bufB(2 * kPageSize, 'b'); - ASSERT_THAT(Write(bufB.c_str(), bufB.size()), - SyscallSucceedsWithValue(bufB.size())); - - // What the mapped portion should now look like. - std::string bufMapped(kPageSize, 'b'); - - // Expect that the mapped portion is consistent. - EXPECT_EQ( - 0, memcmp(reinterpret_cast<void*>(addr), bufMapped.c_str(), kPageSize)); - - // Prepare to read the entire file contents. - ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Expect that the file was fully updated. - std::vector<char> bufFile(2 * kPageSize); - ASSERT_THAT(Read(bufFile.data(), bufFile.size()), - SyscallSucceedsWithValue(bufFile.size())); - // Cast to void* to avoid EXPECT_THAT assuming bufFile.data() is a - // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C - // std::string, possibly overruning the buffer. - EXPECT_THAT(reinterpret_cast<void*>(bufFile.data()), EqualsMemory(bufB)); -} - -// Write data to mapped file. -TEST_F(MMapFileTest, WriteShared) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - size_t len = strlen(kFileContents); - memcpy(reinterpret_cast<void*>(addr), kFileContents, len); - - // The file may not actually be updated until munmap is called. - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - std::vector<char> buf(len); - ASSERT_THAT(Read(buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a - // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C - // string, possibly overruning the buffer. - EXPECT_THAT(reinterpret_cast<void*>(buf.data()), - EqualsMemory(std::string(kFileContents))); -} - -// Write data to portion of mapped page beyond the end of the file. -// These writes are not reflected in the file. -TEST_F(MMapFileTest, WriteSharedBeyondEnd) { - // The file is only half of a page. We map an entire page. Writes to the - // end of the mapping must not be reflected in the file. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - // First half; this is reflected in the file. - std::string first(kPageSize / 2, 'A'); - memcpy(reinterpret_cast<void*>(addr), first.c_str(), first.size()); - - // Second half; this is not reflected in the file. - std::string second(kPageSize / 2, 'B'); - memcpy(reinterpret_cast<void*>(addr + kPageSize / 2), second.c_str(), - second.size()); - - // The file may not actually be updated until munmap is called. - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - // Big enough to fit the entire page, if the writes are mistakenly written to - // the file. - std::vector<char> buf(kPageSize); - - // Only the first half is in the file. - ASSERT_THAT(Read(buf.data(), buf.size()), - SyscallSucceedsWithValue(first.size())); - // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a - // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C - // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C - // std::string, possibly overruning the buffer. - EXPECT_THAT(reinterpret_cast<void*>(buf.data()), EqualsMemory(first)); -} - -// The portion of a mapped page that becomes part of the file after a truncate -// is reflected in the file. -TEST_F(MMapFileTest, WriteSharedTruncateUp) { - // The file is only half of a page. We map an entire page. Writes to the - // end of the mapping must not be reflected in the file. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - // First half; this is reflected in the file. - std::string first(kPageSize / 2, 'A'); - memcpy(reinterpret_cast<void*>(addr), first.c_str(), first.size()); - - // Second half; this is not reflected in the file now (see - // WriteSharedBeyondEnd), but will be after the truncate. - std::string second(kPageSize / 2, 'B'); - memcpy(reinterpret_cast<void*>(addr + kPageSize / 2), second.c_str(), - second.size()); - - // Extend the file to a full page. The second half of the page will be - // reflected in the file. - EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); - - // The file may not actually be updated until munmap is called. - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - // The whole page is in the file. - std::vector<char> buf(kPageSize); - ASSERT_THAT(Read(buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a - // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C - // string, possibly overruning the buffer. - EXPECT_THAT(reinterpret_cast<void*>(buf.data()), EqualsMemory(first)); - EXPECT_THAT(reinterpret_cast<void*>(buf.data() + kPageSize / 2), - EqualsMemory(second)); -} - -TEST_F(MMapFileTest, ReadSharedTruncateDownThenUp) { - // Start from scratch. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Expand the file to a full page and dirty it. - std::string buf(kPageSize, 'a'); - ASSERT_THAT(Write(buf.c_str(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Map the page. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - // Check that the memory contains the file data. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), buf.c_str(), kPageSize)); - - // Truncate down, then up. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); - - // Check that the memory was zeroed. - std::string zeroed(kPageSize, '\0'); - EXPECT_EQ(0, - memcmp(reinterpret_cast<void*>(addr), zeroed.c_str(), kPageSize)); - - // The file may not actually be updated until msync is called. - ASSERT_THAT(Msync(), SyscallSucceeds()); - - // Prepare to read the entire file contents. - ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Expect that the file is fully updated. - std::vector<char> bufFile(kPageSize); - ASSERT_THAT(Read(bufFile.data(), bufFile.size()), - SyscallSucceedsWithValue(bufFile.size())); - EXPECT_EQ(0, memcmp(bufFile.data(), zeroed.c_str(), kPageSize)); -} - -TEST_F(MMapFileTest, WriteSharedTruncateDownThenUp) { - // The file is only half of a page. We map an entire page. Writes to the - // end of the mapping must not be reflected in the file. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - // First half; this will be deleted by truncate(0). - std::string first(kPageSize / 2, 'A'); - memcpy(reinterpret_cast<void*>(addr), first.c_str(), first.size()); - - // Truncate down, then up. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); - - // The whole page is zeroed in memory. - std::string zeroed(kPageSize, '\0'); - EXPECT_EQ(0, - memcmp(reinterpret_cast<void*>(addr), zeroed.c_str(), kPageSize)); - - // The file may not actually be updated until munmap is called. - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - // The whole file is also zeroed. - std::vector<char> buf(kPageSize); - ASSERT_THAT(Read(buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a - // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C - // string, possibly overruning the buffer. - EXPECT_THAT(reinterpret_cast<void*>(buf.data()), EqualsMemory(zeroed)); -} - -TEST_F(MMapFileTest, ReadSharedTruncateSIGBUS) { - SetupGvisorDeathTest(); - - // Start from scratch. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Expand the file to a full page and dirty it. - std::string buf(kPageSize, 'a'); - ASSERT_THAT(Write(buf.c_str(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Map the page. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - // Check that the mapping contains the file data. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), buf.c_str(), kPageSize)); - - // Truncate down. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Accessing the truncated region should cause a SIGBUS. - std::vector<char> in(kPageSize); - EXPECT_EXIT( - std::copy(reinterpret_cast<volatile char*>(addr), - reinterpret_cast<volatile char*>(addr) + kPageSize, in.data()), - ::testing::KilledBySignal(SIGBUS), ""); -} - -TEST_F(MMapFileTest, WriteSharedTruncateSIGBUS) { - SetupGvisorDeathTest(); - - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - // Touch the memory to be sure it really is mapped. - size_t len = strlen(kFileContents); - memcpy(reinterpret_cast<void*>(addr), kFileContents, len); - - // Truncate down. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Accessing the truncated file should cause a SIGBUS. - EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, - reinterpret_cast<volatile char*>(addr)), - ::testing::KilledBySignal(SIGBUS), ""); -} - -TEST_F(MMapFileTest, ReadSharedTruncatePartialPage) { - // Start from scratch. - EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds()); - - // Dirty the file. - std::string buf(kPageSize, 'a'); - ASSERT_THAT(Write(buf.c_str(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Map a page. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - // Truncate to half of the page. - EXPECT_THAT(ftruncate(fd_.get(), kPageSize / 2), SyscallSucceeds()); - - // First half of the page untouched. - EXPECT_EQ(0, - memcmp(reinterpret_cast<void*>(addr), buf.data(), kPageSize / 2)); - - // Second half is zeroed. - std::string zeroed(kPageSize / 2, '\0'); - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize / 2), - zeroed.c_str(), kPageSize / 2)); -} - -// Page can still be accessed and contents are intact after truncating a partial -// page. -TEST_F(MMapFileTest, WriteSharedTruncatePartialPage) { - // Expand the file to a full page. - EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds()); - - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - // Fill the entire page. - std::string contents(kPageSize, 'A'); - memcpy(reinterpret_cast<void*>(addr), contents.c_str(), contents.size()); - - // Truncate half of the page. - EXPECT_THAT(ftruncate(fd_.get(), kPageSize / 2), SyscallSucceeds()); - - // First half of the page untouched. - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), contents.c_str(), - kPageSize / 2)); - - // Second half zeroed. - std::string zeroed(kPageSize / 2, '\0'); - EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize / 2), - zeroed.c_str(), kPageSize / 2)); -} - -// MAP_PRIVATE writes are not carried through to the underlying file. -TEST_F(MMapFileTest, WritePrivate) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, - fd_.get(), 0), - SyscallSucceeds()); - - size_t len = strlen(kFileContents); - memcpy(reinterpret_cast<void*>(addr), kFileContents, len); - - // The file should not be updated, but if it mistakenly is, it may not be - // until after munmap is called. - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - std::vector<char> buf(len); - ASSERT_THAT(Read(buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a - // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C - // string, possibly overruning the buffer. - EXPECT_THAT(reinterpret_cast<void*>(buf.data()), - EqualsMemory(std::string(len, '\0'))); -} - -// SIGBUS raised when reading or writing past end of a mapped file. -TEST_P(MMapFileParamTest, SigBusDeath) { - SetupGvisorDeathTest(); - - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0), - SyscallSucceeds()); - - auto* start = reinterpret_cast<volatile char*>(addr + kPageSize); - - // MMapFileTest makes a file kPageSize/2 long. The entire first page should be - // accessible, but anything beyond it should not. - if (prot() & PROT_WRITE) { - // Write beyond first page. - size_t len = strlen(kFileContents); - EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, start), - ::testing::KilledBySignal(SIGBUS), ""); - } else { - // Read beyond first page. - std::vector<char> in(kPageSize); - EXPECT_EXIT(std::copy(start, start + kPageSize, in.data()), - ::testing::KilledBySignal(SIGBUS), ""); - } -} - -// Tests that SIGBUS is not raised when reading or writing to a file-mapped -// page before EOF, even if part of the mapping extends beyond EOF. -// -// See b/27877699. -TEST_P(MMapFileParamTest, NoSigBusOnPagesBeforeEOF) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0), - SyscallSucceeds()); - - // The test passes if this survives. - auto* start = reinterpret_cast<volatile char*>(addr + (kPageSize / 2) + 1); - size_t len = strlen(kFileContents); - if (prot() & PROT_WRITE) { - std::copy(kFileContents, kFileContents + len, start); - } else { - std::vector<char> in(len); - std::copy(start, start + len, in.data()); - } -} - -// Tests that SIGBUS is not raised when reading or writing from a file-mapped -// page containing EOF, *after* the EOF. -TEST_P(MMapFileParamTest, NoSigBusOnPageContainingEOF) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0), - SyscallSucceeds()); - - // The test passes if this survives. (Technically addr+kPageSize/2 is already - // beyond EOF, but +1 to check for fencepost errors.) - auto* start = reinterpret_cast<volatile char*>(addr + (kPageSize / 2) + 1); - size_t len = strlen(kFileContents); - if (prot() & PROT_WRITE) { - std::copy(kFileContents, kFileContents + len, start); - } else { - std::vector<char> in(len); - std::copy(start, start + len, in.data()); - } -} - -// Tests that reading from writable shared file-mapped pages succeeds. -// -// On most platforms this is trivial, but when the file is mapped via the sentry -// page cache (which does not yet support writing to shared mappings), a bug -// caused reads to fail unnecessarily on such mappings. See b/28913513. -TEST_F(MMapFileTest, ReadingWritableSharedFilePageSucceeds) { - uintptr_t addr; - size_t len = strlen(kFileContents); - - ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - std::vector<char> buf(kPageSize); - // The test passes if this survives. - std::copy(reinterpret_cast<volatile char*>(addr), - reinterpret_cast<volatile char*>(addr) + len, buf.data()); -} - -// Tests that EFAULT is returned when invoking a syscall that requires the OS to -// read past end of file (resulting in a fault in sentry context in the gVisor -// case). See b/28913513. -TEST_F(MMapFileTest, InternalSigBus) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, - fd_.get(), 0), - SyscallSucceeds()); - - // This depends on the fact that gVisor implements pipes internally. - int pipefd[2]; - ASSERT_THAT(pipe(pipefd), SyscallSucceeds()); - EXPECT_THAT( - write(pipefd[1], reinterpret_cast<void*>(addr + kPageSize), kPageSize), - SyscallFailsWithErrno(EFAULT)); - - EXPECT_THAT(close(pipefd[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipefd[1]), SyscallSucceeds()); -} - -// Like InternalSigBus, but test the WriteZerosAt path by reading from -// /dev/zero to a shared mapping (so that the SIGBUS isn't caught during -// copy-on-write breaking). -TEST_F(MMapFileTest, InternalSigBusZeroing) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - - const FileDescriptor dev_zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); - EXPECT_THAT(read(dev_zero.get(), reinterpret_cast<void*>(addr + kPageSize), - kPageSize), - SyscallFailsWithErrno(EFAULT)); -} - -// Checks that mmaps with a length of uint64_t(-PAGE_SIZE + 1) or greater do not -// induce a sentry panic (due to "rounding up" to 0). -TEST_F(MMapTest, HugeLength) { - EXPECT_THAT(Map(0, static_cast<uint64_t>(-kPageSize + 1), PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallFailsWithErrno(ENOMEM)); -} - -// Tests for a specific gVisor MM caching bug. -TEST_F(MMapTest, AccessCOWInvalidatesCachedSegments) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); - auto zero_fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); - - // Get a two-page private mapping and fill it with 1s. - uintptr_t addr; - ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0), - SyscallSucceeds()); - memset(addr_, 1, 2 * kPageSize); - MaybeSave(); - - // Fork to make the mapping copy-on-write. - pid_t const pid = fork(); - if (pid == 0) { - // The child process waits for the parent to SIGKILL it. - while (true) { - pause(); - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto cleanup_child = Cleanup([&] { - EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds()); - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - }); - - // Induce a read-only Access of the first page of the mapping, which will not - // cause a copy. The usermem.Segment should be cached. - ASSERT_THAT(PwriteFd(fd.get(), addr_, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // Induce a writable Access of both pages of the mapping. This should - // invalidate the cached Segment. - ASSERT_THAT(PreadFd(zero_fd.get(), addr_, 2 * kPageSize, 0), - SyscallSucceedsWithValue(2 * kPageSize)); - - // Induce a read-only Access of the first page of the mapping again. It should - // read the 0s that were stored in the mapping by the read from /dev/zero. If - // the read failed to invalidate the cached Segment, it will instead read the - // 1s in the stale page. - ASSERT_THAT(PwriteFd(fd.get(), addr_, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - std::vector<char> buf(kPageSize); - ASSERT_THAT(PreadFd(fd.get(), buf.data(), kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - for (size_t i = 0; i < kPageSize; i++) { - ASSERT_EQ(0, buf[i]) << "at offset " << i; - } -} - -TEST_F(MMapTest, NoReserve) { - const size_t kSize = 10 * 1 << 20; // 10M - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0), - SyscallSucceeds()); - EXPECT_GT(addr, 0); - - // Check that every page can be read/written. Technically, writing to memory - // could SIGSEGV in case there is no more memory available. In gVisor it - // would never happen though because NORESERVE is ignored. In Linux, it's - // possible to fail, but allocation is small enough that it's highly likely - // to succeed. - for (size_t j = 0; j < kSize; j += kPageSize) { - EXPECT_EQ(0, reinterpret_cast<char*>(addr)[j]); - reinterpret_cast<char*>(addr)[j] = j; - } -} - -// Map more than the gVisor page-cache map unit (64k) and ensure that -// it is consistent with reading from the file. -TEST_F(MMapFileTest, Bug38498194) { - // Choose a sufficiently large map unit. - constexpr int kSize = 4 * 1024 * 1024; - EXPECT_THAT(ftruncate(fd_.get(), kSize), SyscallSucceeds()); - - // Map a large enough region so that multiple internal segments - // are created to back the mapping. - uintptr_t addr; - ASSERT_THAT( - addr = Map(0, kSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - - std::vector<char> expect(kSize, 'a'); - std::copy(expect.data(), expect.data() + expect.size(), - reinterpret_cast<volatile char*>(addr)); - - // Trigger writeback for gVisor. In Linux pages stay cached until - // it can't hold onto them anymore. - ASSERT_THAT(Unmap(), SyscallSucceeds()); - - std::vector<char> buf(kSize); - ASSERT_THAT(Read(buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - EXPECT_EQ(buf, expect) << std::string(buf.data(), buf.size()); -} - -// Tests that reading from a file to a memory mapping of the same file does not -// deadlock. See b/34813270. -TEST_F(MMapFileTest, SelfRead) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, - fd_.get(), 0), - SyscallSucceeds()); - EXPECT_THAT(Read(reinterpret_cast<char*>(addr), kPageSize / 2), - SyscallSucceedsWithValue(kPageSize / 2)); - // The resulting file contents are poorly-specified and irrelevant. -} - -// Tests that writing to a file from a memory mapping of the same file does not -// deadlock. Regression test for b/34813270. -TEST_F(MMapFileTest, SelfWrite) { - uintptr_t addr; - ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0), - SyscallSucceeds()); - EXPECT_THAT(Write(reinterpret_cast<char*>(addr), kPageSize / 2), - SyscallSucceedsWithValue(kPageSize / 2)); - // The resulting file contents are poorly-specified and irrelevant. -} - -TEST(MMapDeathTest, TruncateAfterCOWBreak) { - SetupGvisorDeathTest(); - - // Create and map a single-page file. - auto const temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDWR)); - ASSERT_THAT(ftruncate(fd.get(), kPageSize), SyscallSucceeds()); - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd.get(), 0)); - - // Write to this mapping, causing the page to be copied for write. - memset(mapping.ptr(), 'a', mapping.len()); - MaybeSave(); // Trigger a co-operative save cycle. - - // Truncate the file and expect it to invalidate the copied page. - ASSERT_THAT(ftruncate(fd.get(), 0), SyscallSucceeds()); - EXPECT_EXIT(*reinterpret_cast<volatile char*>(mapping.ptr()), - ::testing::KilledBySignal(SIGBUS), ""); -} - -// Regression test for #147. -TEST(MMapNoFixtureTest, MapReadOnlyAfterCreateWriteOnly) { - std::string filename = NewTempAbsPath(); - - // We have to create the file O_RDONLY to reproduce the bug because - // fsgofer.localFile.Create() silently upgrades O_WRONLY to O_RDWR, causing - // the cached "write-only" FD to be read/write and therefore usable by mmap(). - auto const ro_fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(filename, O_RDONLY | O_CREAT | O_EXCL, 0666)); - - // Get a write-only FD for the same file, which should be ignored by mmap() - // (but isn't in #147). - auto const wo_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_WRONLY)); - ASSERT_THAT(ftruncate(wo_fd.get(), kPageSize), SyscallSucceeds()); - - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, ro_fd.get(), 0)); - std::vector<char> buf(kPageSize); - // The test passes if this survives. - std::copy(static_cast<char*>(mapping.ptr()), - static_cast<char*>(mapping.endptr()), buf.data()); -} - -// Conditional on MAP_32BIT. -// This flag is supported only on x86-64, for 64-bit programs. -#ifdef __x86_64__ - -TEST(MMapNoFixtureTest, Map32Bit) { - auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE | MAP_32BIT)); - EXPECT_LT(mapping.addr(), static_cast<uintptr_t>(1) << 32); - EXPECT_LE(mapping.endaddr(), static_cast<uintptr_t>(1) << 32); -} - -#endif // defined(__x86_64__) - -INSTANTIATE_TEST_SUITE_P( - ReadWriteSharedPrivate, MMapFileParamTest, - ::testing::Combine(::testing::ValuesIn({ - PROT_READ, - PROT_WRITE, - PROT_READ | PROT_WRITE, - }), - ::testing::ValuesIn({MAP_SHARED, MAP_PRIVATE}))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc deleted file mode 100644 index 15b645fb7..000000000 --- a/test/syscalls/linux/mount.cc +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <sys/mount.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <functional> -#include <memory> -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/mount_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(MountTest, MountBadFilesystem) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - // Linux expects a valid target before it checks the file system name. - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(mount("", dir.path().c_str(), "foobar", 0, ""), - SyscallFailsWithErrno(ENODEV)); -} - -TEST(MountTest, MountInvalidTarget) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = NewTempAbsPath(); - EXPECT_THAT(mount("", dir.c_str(), "tmpfs", 0, ""), - SyscallFailsWithErrno(ENOENT)); -} - -TEST(MountTest, MountPermDenied) { - // Clear CAP_SYS_ADMIN. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); - } - - // Linux expects a valid target before checking capability. - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(mount("", dir.path().c_str(), "", 0, ""), - SyscallFailsWithErrno(EPERM)); -} - -TEST(MountTest, UmountPermDenied) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const mount = - ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "", 0)); - - // Drop privileges in another thread, so we can still unmount the mounted - // directory. - ScopedThread([&]() { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); - EXPECT_THAT(umount(dir.path().c_str()), SyscallFailsWithErrno(EPERM)); - }); -} - -TEST(MountTest, MountOverBusy) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); - - // Should be able to mount over a busy directory. - ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "", 0)); -} - -TEST(MountTest, OpenFileBusy) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", 0, "mode=0700", 0)); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); - - // An open file should prevent unmounting. - EXPECT_THAT(umount(dir.path().c_str()), SyscallFailsWithErrno(EBUSY)); -} - -TEST(MountTest, UmountDetach) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - // structure: - // - // dir (mount point) - // subdir - // file - // - // We show that we can walk around in the mount after detach-unmount dir. - // - // We show that even though dir is unreachable from outside the mount, we can - // still reach dir's (former) parent! - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - const struct stat before = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - auto mount = - ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "mode=0700", - /* umountflags= */ MNT_DETACH)); - const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - EXPECT_FALSE(before.st_dev == after.st_dev && before.st_ino == after.st_ino) - << "mount point has device number " << before.st_dev - << " and inode number " << before.st_ino << " before and after mount"; - - // Create files in the new mount. - constexpr char kContents[] = "no no no"; - auto const subdir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - auto const file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(dir.path(), kContents, 0777)); - - auto const dir_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(subdir.path(), O_RDONLY | O_DIRECTORY)); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - // Unmount the tmpfs. - mount.Release()(); - - // Inode numbers for gofer-accessed files may change across save/restore. - // - // For overlayfs, if xino option is not enabled and if all overlayfs layers do - // not belong to the same filesystem then "the value of st_ino for directory - // objects may not be persistent and could change even while the overlay - // filesystem is mounted." -- Documentation/filesystems/overlayfs.txt - if (!IsRunningWithSaveRestore() && - !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) { - const struct stat after2 = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - EXPECT_EQ(before.st_ino, after2.st_ino); - } - - // Can still read file after unmounting. - std::vector<char> buf(sizeof(kContents)); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), SyscallSucceeds()); - - // Walk to dir. - auto const mounted_dir = ASSERT_NO_ERRNO_AND_VALUE( - OpenAt(dir_fd.get(), "..", O_DIRECTORY | O_RDONLY)); - // Walk to dir/file. - auto const fd_again = ASSERT_NO_ERRNO_AND_VALUE( - OpenAt(mounted_dir.get(), std::string(Basename(file.path())), O_RDONLY)); - - std::vector<char> buf2(sizeof(kContents)); - EXPECT_THAT(ReadFd(fd_again.get(), buf2.data(), buf2.size()), - SyscallSucceeds()); - EXPECT_EQ(buf, buf2); - - // Walking outside the unmounted realm should still work, too! - auto const dir_parent = ASSERT_NO_ERRNO_AND_VALUE( - OpenAt(mounted_dir.get(), "..", O_DIRECTORY | O_RDONLY)); -} - -TEST(MountTest, ActiveSubmountBusy) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const mount1 = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", 0, "mode=0700", 0)); - - auto const dir2 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - auto const mount2 = - ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir2.path(), "tmpfs", 0, "", 0)); - - // Since dir now has an active submount, should not be able to unmount. - EXPECT_THAT(umount(dir.path().c_str()), SyscallFailsWithErrno(EBUSY)); -} - -TEST(MountTest, MountTmpfs) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // NOTE(b/129868551): Inode IDs are only stable across S/R if we have an open - // FD for that inode. Since we are going to compare inode IDs below, get a - // FileDescriptor for this directory here, which will be closed automatically - // at the end of the test. - auto const fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY, O_RDONLY)); - - const struct stat before = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - - { - auto const mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", 0, "mode=0700", 0)); - - const struct stat s = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - EXPECT_EQ(s.st_mode, S_IFDIR | 0700); - EXPECT_FALSE(before.st_dev == s.st_dev && before.st_ino == s.st_ino) - << "mount point has device number " << before.st_dev - << " and inode number " << before.st_ino << " before and after mount"; - - EXPECT_NO_ERRNO(Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); - } - - // Now that dir is unmounted again, we should have the old inode back. - // - // Inode numbers for gofer-accessed files may change across save/restore. - // - // For overlayfs, if xino option is not enabled and if all overlayfs layers do - // not belong to the same filesystem then "the value of st_ino for directory - // objects may not be persistent and could change even while the overlay - // filesystem is mounted." -- Documentation/filesystems/overlayfs.txt - if (!IsRunningWithSaveRestore() && - !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) { - const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - EXPECT_EQ(before.st_ino, after.st_ino); - } -} - -TEST(MountTest, MountTmpfsMagicValIgnored) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - auto const mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", MS_MGC_VAL, "mode=0700", 0)); -} - -// Passing nullptr to data is equivalent to "". -TEST(MountTest, NullData) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - EXPECT_THAT(mount("", dir.path().c_str(), "tmpfs", 0, nullptr), - SyscallSucceeds()); - EXPECT_THAT(umount2(dir.path().c_str(), 0), SyscallSucceeds()); -} - -TEST(MountTest, MountReadonly) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", MS_RDONLY, "mode=0777", 0)); - - const struct stat s = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - EXPECT_EQ(s.st_mode, S_IFDIR | 0777); - - std::string const filename = JoinPath(dir.path(), "foo"); - EXPECT_THAT(open(filename.c_str(), O_RDWR | O_CREAT, 0777), - SyscallFailsWithErrno(EROFS)); -} - -PosixErrorOr<absl::Time> ATime(absl::string_view file) { - struct stat s = {}; - if (stat(std::string(file).c_str(), &s) == -1) { - return PosixError(errno, "stat failed"); - } - return absl::TimeFromTimespec(s.st_atim); -} - -TEST(MountTest, MountNoAtime) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", MS_NOATIME, "mode=0777", 0)); - - std::string const contents = "No no no, don't follow the instructions!"; - auto const file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(dir.path(), contents, 0777)); - - absl::Time const before = ASSERT_NO_ERRNO_AND_VALUE(ATime(file.path())); - - // Reading from the file should change the atime, but the MS_NOATIME flag - // should prevent that. - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - char buf[100]; - int read_n; - ASSERT_THAT(read_n = read(fd.get(), buf, sizeof(buf)), SyscallSucceeds()); - EXPECT_EQ(std::string(buf, read_n), contents); - - absl::Time const after = ASSERT_NO_ERRNO_AND_VALUE(ATime(file.path())); - - // Expect that atime hasn't changed. - EXPECT_EQ(before, after); -} - -TEST(MountTest, MountNoExec) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const mount = ASSERT_NO_ERRNO_AND_VALUE( - Mount("", dir.path(), "tmpfs", MS_NOEXEC, "mode=0777", 0)); - - std::string const contents = "No no no, don't follow the instructions!"; - auto const file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(dir.path(), contents, 0777)); - - int execve_errno; - ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(file.path(), {}, {}, nullptr, &execve_errno)); - EXPECT_EQ(execve_errno, EACCES); -} - -TEST(MountTest, RenameRemoveMountPoint) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto const dir_parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto const dir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir_parent.path())); - auto const new_dir = NewTempAbsPath(); - - auto const mount = - ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "", 0)); - - ASSERT_THAT(rename(dir.path().c_str(), new_dir.c_str()), - SyscallFailsWithErrno(EBUSY)); - - ASSERT_THAT(rmdir(dir.path().c_str()), SyscallFailsWithErrno(EBUSY)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/mremap.cc b/test/syscalls/linux/mremap.cc deleted file mode 100644 index f0e5f7d82..000000000 --- a/test/syscalls/linux/mremap.cc +++ /dev/null @@ -1,492 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <string.h> -#include <sys/mman.h> - -#include <string> - -#include "gmock/gmock.h" -#include "absl/strings/string_view.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::_; - -namespace gvisor { -namespace testing { - -namespace { - -// Fixture for mremap tests parameterized by mmap flags. -using MremapParamTest = ::testing::TestWithParam<int>; - -TEST_P(MremapParamTest, Noop) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - - ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize, 0, nullptr), - IsPosixErrorOkAndHolds(m.ptr())); - EXPECT_TRUE(IsMapped(m.addr())); -} - -TEST_P(MremapParamTest, InPlace_ShrinkingWholeVMA) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // N.B. we must be in a single-threaded subprocess to ensure a - // background thread doesn't concurrently map the second page. - void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == m.ptr()); - MaybeSave(); - - TEST_CHECK(IsMapped(m.addr())); - TEST_CHECK(!IsMapped(m.addr() + kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, InPlace_ShrinkingPartialVMA) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == m.ptr()); - MaybeSave(); - - TEST_CHECK(IsMapped(m.addr())); - TEST_CHECK(!IsMapped(m.addr() + kPageSize)); - TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, InPlace_ShrinkingAcrossVMAs) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam())); - // Changing permissions on the first page forces it to become a separate vma. - ASSERT_THAT(mprotect(m.ptr(), kPageSize, PROT_NONE), SyscallSucceeds()); - - const auto rest = [&] { - // Both old_size and new_size now span two vmas; mremap - // shouldn't care. - void* addr = mremap(m.ptr(), 3 * kPageSize, 2 * kPageSize, 0, nullptr); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == m.ptr()); - MaybeSave(); - - TEST_CHECK(IsMapped(m.addr())); - TEST_CHECK(IsMapped(m.addr() + kPageSize)); - TEST_CHECK(!IsMapped(m.addr() + 2 * kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, InPlace_ExpansionSuccess) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap the second page so that the first can be expanded back into it. - // - // N.B. we must be in a single-threaded subprocess to ensure a - // background thread doesn't concurrently map this page. - TEST_PCHECK( - munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0); - MaybeSave(); - - void* addr = mremap(m.ptr(), kPageSize, 2 * kPageSize, 0, nullptr); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == m.ptr()); - MaybeSave(); - - TEST_CHECK(IsMapped(m.addr())); - TEST_CHECK(IsMapped(m.addr() + kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, InPlace_ExpansionFailure) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap the second page, leaving a one-page hole. Trying to expand the - // first page to three pages should fail since the original third page - // is still mapped. - TEST_PCHECK( - munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0); - MaybeSave(); - - void* addr = mremap(m.ptr(), kPageSize, 3 * kPageSize, 0, nullptr); - TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded"); - TEST_PCHECK_MSG(errno == ENOMEM, "mremap failed with wrong errno"); - MaybeSave(); - - TEST_CHECK(IsMapped(m.addr())); - TEST_CHECK(!IsMapped(m.addr() + kPageSize)); - TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, MayMove_Expansion) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap the second page, leaving a one-page hole. Trying to expand the - // first page to three pages with MREMAP_MAYMOVE should force the - // mapping to be relocated since the original third page is still - // mapped. - TEST_PCHECK( - munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0); - MaybeSave(); - - void* addr2 = - mremap(m.ptr(), kPageSize, 3 * kPageSize, MREMAP_MAYMOVE, nullptr); - TEST_PCHECK_MSG(addr2 != MAP_FAILED, "mremap failed"); - MaybeSave(); - - const Mapping m2 = Mapping(addr2, 3 * kPageSize); - TEST_CHECK(m.addr() != m2.addr()); - - TEST_CHECK(!IsMapped(m.addr())); - TEST_CHECK(!IsMapped(m.addr() + kPageSize)); - TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize)); - TEST_CHECK(IsMapped(m2.addr())); - TEST_CHECK(IsMapped(m2.addr() + kPageSize)); - TEST_CHECK(IsMapped(m2.addr() + 2 * kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, Fixed_SourceAndDestinationCannotOverlap) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - - ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, m.ptr()), - PosixErrorIs(EINVAL, _)); - EXPECT_TRUE(IsMapped(m.addr())); -} - -TEST_P(MremapParamTest, Fixed_SameSize) { - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap dst to create a hole. - TEST_PCHECK(munmap(dst.ptr(), kPageSize) == 0); - MaybeSave(); - - void* addr = mremap(src.ptr(), kPageSize, kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == dst.ptr()); - MaybeSave(); - - TEST_CHECK(!IsMapped(src.addr())); - TEST_CHECK(IsMapped(dst.addr())); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, Fixed_SameSize_Unmapping) { - // Like the Fixed_SameSize case, but expect mremap to unmap the destination - // automatically. - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - void* addr = mremap(src.ptr(), kPageSize, kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == dst.ptr()); - MaybeSave(); - - TEST_CHECK(!IsMapped(src.addr())); - TEST_CHECK(IsMapped(dst.addr())); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, Fixed_ShrinkingWholeVMA) { - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap dst so we can check that mremap does not keep the - // second page. - TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0); - MaybeSave(); - - void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == dst.ptr()); - MaybeSave(); - - TEST_CHECK(!IsMapped(src.addr())); - TEST_CHECK(!IsMapped(src.addr() + kPageSize)); - TEST_CHECK(IsMapped(dst.addr())); - TEST_CHECK(!IsMapped(dst.addr() + kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, Fixed_ShrinkingPartialVMA) { - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam())); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap dst so we can check that mremap does not keep the - // second page. - TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0); - MaybeSave(); - - void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == dst.ptr()); - MaybeSave(); - - TEST_CHECK(!IsMapped(src.addr())); - TEST_CHECK(!IsMapped(src.addr() + kPageSize)); - TEST_CHECK(IsMapped(src.addr() + 2 * kPageSize)); - TEST_CHECK(IsMapped(dst.addr())); - TEST_CHECK(!IsMapped(dst.addr() + kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, Fixed_ShrinkingAcrossVMAs) { - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam())); - // Changing permissions on the first page forces it to become a separate vma. - ASSERT_THAT(mprotect(src.ptr(), kPageSize, PROT_NONE), SyscallSucceeds()); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unlike flags=0, MREMAP_FIXED requires that [old_address, - // old_address+new_size) only spans a single vma. - void* addr = mremap(src.ptr(), 3 * kPageSize, 2 * kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); - TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded"); - TEST_PCHECK_MSG(errno == EFAULT, "mremap failed with wrong errno"); - MaybeSave(); - - TEST_CHECK(IsMapped(src.addr())); - TEST_CHECK(IsMapped(src.addr() + kPageSize)); - // Despite failing, mremap should have unmapped [old_address+new_size, - // old_address+old_size) (i.e. the third page). - TEST_CHECK(!IsMapped(src.addr() + 2 * kPageSize)); - // Despite failing, mremap should have unmapped the destination pages. - TEST_CHECK(!IsMapped(dst.addr())); - TEST_CHECK(!IsMapped(dst.addr() + kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST_P(MremapParamTest, Fixed_Expansion) { - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam())); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam())); - - const auto rest = [&] { - // Unmap dst so we can check that mremap actually maps all pages - // at the destination. - TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0); - MaybeSave(); - - void* addr = mremap(src.ptr(), kPageSize, 2 * kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()); - TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed"); - TEST_CHECK(addr == dst.ptr()); - MaybeSave(); - - TEST_CHECK(!IsMapped(src.addr())); - TEST_CHECK(IsMapped(dst.addr())); - TEST_CHECK(IsMapped(dst.addr() + kPageSize)); - }; - - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -INSTANTIATE_TEST_SUITE_P(PrivateShared, MremapParamTest, - ::testing::Values(MAP_PRIVATE, MAP_SHARED)); - -// mremap with old_size == 0 only works with MAP_SHARED after Linux 4.14 -// (dba58d3b8c50 "mm/mremap: fail map duplication attempts for private -// mappings"). - -TEST(MremapTest, InPlace_Copy) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED)); - EXPECT_THAT(Mremap(m.ptr(), 0, kPageSize, 0, nullptr), - PosixErrorIs(ENOMEM, _)); -} - -TEST(MremapTest, MayMove_Copy) { - Mapping const m = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED)); - - // Remainder of this test executes in a subprocess to ensure that if mremap - // incorrectly removes m, it is not remapped by another thread. - const auto rest = [&] { - void* ptr = mremap(m.ptr(), 0, kPageSize, MREMAP_MAYMOVE, nullptr); - MaybeSave(); - TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed"); - TEST_CHECK(ptr != m.ptr()); - TEST_CHECK(IsMapped(m.addr())); - TEST_CHECK(IsMapped(reinterpret_cast<uintptr_t>(ptr))); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -TEST(MremapTest, MustMove_Copy) { - Mapping const src = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED)); - Mapping const dst = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); - - // Remainder of this test executes in a subprocess to ensure that if mremap - // incorrectly removes src, it is not remapped by another thread. - const auto rest = [&] { - void* ptr = mremap(src.ptr(), 0, kPageSize, MREMAP_MAYMOVE | MREMAP_FIXED, - dst.ptr()); - MaybeSave(); - TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed"); - TEST_CHECK(ptr == dst.ptr()); - TEST_CHECK(IsMapped(src.addr())); - TEST_CHECK(IsMapped(dst.addr())); - }; - EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); -} - -void ExpectAllBytesAre(absl::string_view v, char c) { - for (size_t i = 0; i < v.size(); i++) { - ASSERT_EQ(v[i], c) << "at offset " << i; - } -} - -TEST(MremapTest, ExpansionPreservesCOWPagesAndExposesNewFilePages) { - // Create a file with 3 pages. The first is filled with 'a', the second is - // filled with 'b', and the third is filled with 'c'. - TempPath const file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'a').c_str(), kPageSize), - SyscallSucceedsWithValue(kPageSize)); - ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'b').c_str(), kPageSize), - SyscallSucceedsWithValue(kPageSize)); - ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'c').c_str(), kPageSize), - SyscallSucceedsWithValue(kPageSize)); - - // Create a private mapping of the first 2 pages, and fill the second page - // with 'd'. - Mapping const src = ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, 2 * kPageSize, - PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd.get(), 0)); - memset(reinterpret_cast<void*>(src.addr() + kPageSize), 'd', kPageSize); - MaybeSave(); - - // Move the mapping while expanding it to 3 pages. The resulting mapping - // should contain the original first page of the file (filled with 'a'), - // followed by the private copy of the second page (filled with 'd'), followed - // by the newly-mapped third page of the file (filled with 'c'). - Mapping const dst = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(3 * kPageSize, PROT_NONE, MAP_PRIVATE)); - ASSERT_THAT(Mremap(src.ptr(), 2 * kPageSize, 3 * kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()), - IsPosixErrorOkAndHolds(dst.ptr())); - auto const v = dst.view(); - ExpectAllBytesAre(v.substr(0, kPageSize), 'a'); - ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'd'); - ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), 'c'); -} - -TEST(MremapDeathTest, SharedAnon) { - SetupGvisorDeathTest(); - - // Reserve 4 pages of address space. - Mapping const reserved = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(4 * kPageSize, PROT_NONE, MAP_PRIVATE)); - - // Create a 2-page shared anonymous mapping at the beginning of the - // reservation. Fill the first page with 'a' and the second with 'b'. - Mapping const m = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(reserved.ptr(), 2 * kPageSize, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0)); - memset(m.ptr(), 'a', kPageSize); - memset(reinterpret_cast<void*>(m.addr() + kPageSize), 'b', kPageSize); - MaybeSave(); - - // Shrink the mapping to 1 page in-place. - ASSERT_THAT(Mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, m.ptr()), - IsPosixErrorOkAndHolds(m.ptr())); - - // Expand the mapping to 3 pages, moving it forward by 1 page in the process - // since the old and new mappings can't overlap. - void* const new_m = reinterpret_cast<void*>(m.addr() + kPageSize); - ASSERT_THAT(Mremap(m.ptr(), kPageSize, 3 * kPageSize, - MREMAP_MAYMOVE | MREMAP_FIXED, new_m), - IsPosixErrorOkAndHolds(new_m)); - - // The first 2 pages of the mapping should still contain the data we wrote - // (i.e. shrinking should not have discarded the second page's data), while - // touching the third page should raise SIGBUS. - auto const v = - absl::string_view(static_cast<char const*>(new_m), 3 * kPageSize); - ExpectAllBytesAre(v.substr(0, kPageSize), 'a'); - ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'b'); - EXPECT_EXIT(ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), '\0'), - ::testing::KilledBySignal(SIGBUS), ""); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/msync.cc b/test/syscalls/linux/msync.cc deleted file mode 100644 index 2b2b6aef9..000000000 --- a/test/syscalls/linux/msync.cc +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/mman.h> -#include <unistd.h> - -#include <functional> -#include <string> -#include <utility> -#include <vector> - -#include "test/util/file_descriptor.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Parameters for msync tests. Use a std::tuple so we can use -// ::testing::Combine. -using MsyncTestParam = - std::tuple<int, // msync flags - std::function<PosixErrorOr<Mapping>()> // returns mapping to - // msync - >; - -class MsyncParameterizedTest : public ::testing::TestWithParam<MsyncTestParam> { - protected: - int msync_flags() const { return std::get<0>(GetParam()); } - - PosixErrorOr<Mapping> GetMapping() const { return std::get<1>(GetParam())(); } -}; - -// All valid msync(2) flag combinations, not including MS_INVALIDATE. ("Linux -// permits a call to msync() that specifies neither [MS_SYNC or MS_ASYNC], with -// semantics that are (currently) equivalent to specifying MS_ASYNC." - -// msync(2)) -constexpr std::initializer_list<int> kMsyncFlags = {MS_SYNC, MS_ASYNC, 0}; - -// Returns functions that return mappings that should be successfully -// msync()able. -std::vector<std::function<PosixErrorOr<Mapping>()>> SyncableMappings() { - std::vector<std::function<PosixErrorOr<Mapping>()>> funcs; - for (bool const writable : {false, true}) { - 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([=]() -> PosixErrorOr<Mapping> { - std::string const path = NewTempAbsPath(); - ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, oflags, 0644)); - // Don't unlink the file since that breaks save/restore. Just let the - // test infrastructure clean up all of our temporary files when we're - // done. - return Mmap(nullptr, kPageSize, prot, mflags, fd.get(), 0); - }); - } - } - return funcs; -} - -PosixErrorOr<Mapping> NoMappings() { - return PosixError(EINVAL, "unexpected attempt to create a mapping"); -} - -// "Fixture" for msync tests that hold for all valid flags, but do not create -// mappings. -using MsyncNoMappingTest = MsyncParameterizedTest; - -TEST_P(MsyncNoMappingTest, UnmappedAddressWithZeroLengthSucceeds) { - EXPECT_THAT(msync(nullptr, 0, msync_flags()), SyscallSucceeds()); -} - -TEST_P(MsyncNoMappingTest, UnmappedAddressWithNonzeroLengthFails) { - EXPECT_THAT(msync(nullptr, kPageSize, msync_flags()), - SyscallFailsWithErrno(ENOMEM)); -} - -INSTANTIATE_TEST_SUITE_P(All, MsyncNoMappingTest, - ::testing::Combine(::testing::ValuesIn(kMsyncFlags), - ::testing::Values(NoMappings))); - -// "Fixture" for msync tests that are not parameterized by msync flags, but do -// create mappings. -using MsyncNoFlagsTest = MsyncParameterizedTest; - -TEST_P(MsyncNoFlagsTest, BothSyncAndAsyncFails) { - auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); - EXPECT_THAT(msync(m.ptr(), m.len(), MS_SYNC | MS_ASYNC), - SyscallFailsWithErrno(EINVAL)); -} - -INSTANTIATE_TEST_SUITE_P( - All, MsyncNoFlagsTest, - ::testing::Combine(::testing::Values(0), // ignored - ::testing::ValuesIn(SyncableMappings()))); - -// "Fixture" for msync tests parameterized by both msync flags and sources of -// mappings. -using MsyncFullParamTest = MsyncParameterizedTest; - -TEST_P(MsyncFullParamTest, NormallySucceeds) { - auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); - EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags()), SyscallSucceeds()); -} - -TEST_P(MsyncFullParamTest, UnalignedLengthSucceeds) { - auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); - EXPECT_THAT(msync(m.ptr(), m.len() - 1, msync_flags()), SyscallSucceeds()); -} - -TEST_P(MsyncFullParamTest, UnalignedAddressFails) { - auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); - EXPECT_THAT( - msync(reinterpret_cast<void*>(m.addr() + 1), m.len() - 1, msync_flags()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(MsyncFullParamTest, InvalidateUnlockedSucceeds) { - auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); - EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags() | MS_INVALIDATE), - SyscallSucceeds()); -} - -// The test for MS_INVALIDATE on mlocked pages is in mlock.cc since it requires -// probing for mlock support. - -INSTANTIATE_TEST_SUITE_P( - All, MsyncFullParamTest, - ::testing::Combine(::testing::ValuesIn(kMsyncFlags), - ::testing::ValuesIn(SyncableMappings()))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/munmap.cc b/test/syscalls/linux/munmap.cc deleted file mode 100644 index 067241f4d..000000000 --- a/test/syscalls/linux/munmap.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/mman.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class MunmapTest : public ::testing::Test { - protected: - void SetUp() override { - m_ = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(MAP_FAILED, m_); - } - - void* m_ = nullptr; -}; - -TEST_F(MunmapTest, HappyCase) { - EXPECT_THAT(munmap(m_, kPageSize), SyscallSucceeds()); -} - -TEST_F(MunmapTest, ZeroLength) { - EXPECT_THAT(munmap(m_, 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(MunmapTest, LastPageRoundUp) { - // Attempt to unmap up to and including the last page. - EXPECT_THAT(munmap(m_, static_cast<size_t>(-kPageSize + 1)), - SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/network_namespace.cc b/test/syscalls/linux/network_namespace.cc deleted file mode 100644 index 133fdecf0..000000000 --- a/test/syscalls/linux/network_namespace.cc +++ /dev/null @@ -1,52 +0,0 @@ -// 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 <net/if.h> -#include <sched.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_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 { - -TEST(NetworkNamespaceTest, LoopbackExists) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - ScopedThread t([&] { - ASSERT_THAT(unshare(CLONE_NEWNET), SyscallSucceedsWithValue(0)); - - // TODO(gvisor.dev/issue/1833): Update this to test that only "lo" exists. - // Check loopback device exists. - int sock = socket(AF_INET, SOCK_DGRAM, 0); - ASSERT_THAT(sock, SyscallSucceeds()); - struct ifreq ifr; - strncpy(ifr.ifr_name, "lo", IFNAMSIZ); - EXPECT_THAT(ioctl(sock, SIOCGIFINDEX, &ifr), SyscallSucceeds()) - << "lo cannot be found"; - }); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc deleted file mode 100644 index e65ffee8f..000000000 --- a/test/syscalls/linux/open.cc +++ /dev/null @@ -1,534 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <linux/capability.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/syscalls/linux/file_base.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// This test is currently very rudimentary. -// -// There are plenty of extra cases to cover once the sentry supports them. -// -// Different types of opens: -// * O_CREAT -// * O_DIRECTORY -// * O_NOFOLLOW -// * O_PATH -// -// Special operations on open: -// * O_EXCL -// -// Special files: -// * Blocking behavior for a named pipe. -// -// Different errors: -// * EACCES -// * EEXIST -// * ENAMETOOLONG -// * ELOOP -// * ENOTDIR -// * EPERM -class OpenTest : public FileTest { - void SetUp() override { - FileTest::SetUp(); - - ASSERT_THAT( - write(test_file_fd_.get(), test_data_.c_str(), test_data_.length()), - SyscallSucceedsWithValue(test_data_.length())); - EXPECT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), SyscallSucceeds()); - } - - public: - const std::string test_data_ = "hello world\n"; -}; - -TEST_F(OpenTest, OTrunc) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT(open(dir.path().c_str(), O_TRUNC, 0666), - SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(OpenTest, OTruncAndReadOnlyDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT(open(dir.path().c_str(), O_TRUNC | O_RDONLY, 0666), - SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(OpenTest, OTruncAndReadOnlyFile) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto path = JoinPath(dir.path(), "foo"); - EXPECT_NO_ERRNO(Open(path, O_RDWR | O_CREAT, 0666)); - EXPECT_NO_ERRNO(Open(path, O_TRUNC | O_RDONLY, 0666)); -} - -TEST_F(OpenTest, OCreateDirectory) { - SKIP_IF(IsRunningWithVFS1()); - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Normal case: existing directory. - ASSERT_THAT(open(dir.path().c_str(), O_RDWR | O_CREAT, 0666), - SyscallFailsWithErrno(EISDIR)); - // Trailing separator on existing directory. - ASSERT_THAT(open(dir.path().append("/").c_str(), O_RDWR | O_CREAT, 0666), - SyscallFailsWithErrno(EISDIR)); - // Trailing separator on non-existing directory. - ASSERT_THAT(open(JoinPath(dir.path(), "non-existent").append("/").c_str(), - O_RDWR | O_CREAT, 0666), - SyscallFailsWithErrno(EISDIR)); - // "." special case. - ASSERT_THAT(open(JoinPath(dir.path(), ".").c_str(), O_RDWR | O_CREAT, 0666), - SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(OpenTest, MustCreateExisting) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Existing directory. - ASSERT_THAT(open(dir.path().c_str(), O_RDWR | O_CREAT | O_EXCL, 0666), - SyscallFailsWithErrno(EEXIST)); - - // Existing file. - auto newFile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - ASSERT_THAT(open(newFile.path().c_str(), O_RDWR | O_CREAT | O_EXCL, 0666), - SyscallFailsWithErrno(EEXIST)); -} - -TEST_F(OpenTest, ReadOnly) { - char buf; - const FileDescriptor ro_file = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - - EXPECT_THAT(read(ro_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_THAT(lseek(ro_file.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(write(ro_file.get(), &buf, 1), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(OpenTest, WriteOnly) { - char buf; - const FileDescriptor wo_file = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY)); - - EXPECT_THAT(read(wo_file.get(), &buf, 1), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(lseek(wo_file.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(write(wo_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); -} - -TEST_F(OpenTest, CreateWithAppend) { - std::string data = "text"; - std::string new_file = NewTempAbsPath(); - const FileDescriptor file = ASSERT_NO_ERRNO_AND_VALUE( - Open(new_file, O_WRONLY | O_APPEND | O_CREAT, 0666)); - EXPECT_THAT(write(file.get(), data.c_str(), data.size()), - SyscallSucceedsWithValue(data.size())); - EXPECT_THAT(lseek(file.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(write(file.get(), data.c_str(), data.size()), - SyscallSucceedsWithValue(data.size())); - - // Check that the size of the file is correct and that the offset has been - // incremented to that size. - struct stat s0; - EXPECT_THAT(fstat(file.get(), &s0), SyscallSucceeds()); - EXPECT_EQ(s0.st_size, 2 * data.size()); - EXPECT_THAT(lseek(file.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(2 * data.size())); -} - -TEST_F(OpenTest, ReadWrite) { - char buf; - const FileDescriptor rw_file = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - EXPECT_THAT(read(rw_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); - EXPECT_THAT(lseek(rw_file.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(write(rw_file.get(), &buf, 1), SyscallSucceedsWithValue(1)); -} - -TEST_F(OpenTest, RelPath) { - auto name = std::string(Basename(test_file_name_)); - - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name, O_RDONLY)); -} - -TEST_F(OpenTest, AbsPath) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); -} - -TEST_F(OpenTest, AtRelPath) { - auto name = std::string(Basename(test_file_name_)); - const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( - Open(GetAbsoluteTestTmpdir(), O_RDONLY | O_DIRECTORY)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAt(dirfd.get(), name, O_RDONLY)); -} - -TEST_F(OpenTest, AtAbsPath) { - const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( - Open(GetAbsoluteTestTmpdir(), O_RDONLY | O_DIRECTORY)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAt(dirfd.get(), test_file_name_, O_RDONLY)); -} - -TEST_F(OpenTest, OpenNoFollowSymlink) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string link_path = JoinPath(dir.path().c_str(), "link"); - ASSERT_THAT(symlink(test_file_name_.c_str(), link_path.c_str()), - SyscallSucceeds()); - auto cleanup = Cleanup([link_path]() { - EXPECT_THAT(unlink(link_path.c_str()), SyscallSucceeds()); - }); - - // Open will succeed without O_NOFOLLOW and fails with O_NOFOLLOW. - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open(link_path, O_RDONLY)); - ASSERT_THAT(open(link_path.c_str(), O_RDONLY | O_NOFOLLOW), - SyscallFailsWithErrno(ELOOP)); -} - -TEST_F(OpenTest, OpenNoFollowStillFollowsLinksInPath) { - // We will create the following structure: - // tmp_folder/real_folder/file - // tmp_folder/sym_folder -> tmp_folder/real_folder - // - // We will then open tmp_folder/sym_folder/file with O_NOFOLLOW and it - // should succeed as O_NOFOLLOW only applies to the final path component. - auto tmp_path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto sym_path = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), tmp_path.path())); - auto file_path = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmp_path.path())); - - auto path_via_symlink = JoinPath(sym_path.path(), Basename(file_path.path())); - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open(path_via_symlink, O_RDONLY | O_NOFOLLOW)); -} - -// Test that open(2) can follow symlinks that point back to the same tree. -// Test sets up files as follows: -// root/child/symlink => redirects to ../.. -// root/child/target => regular file -// -// open("root/child/symlink/root/child/file") -TEST_F(OpenTest, SymlinkRecurse) { - auto root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root.path())); - auto symlink = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(child.path(), "../..")); - auto target = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(child.path(), "abc", 0644)); - auto path_via_symlink = - JoinPath(symlink.path(), Basename(root.path()), Basename(child.path()), - Basename(target.path())); - const auto contents = - ASSERT_NO_ERRNO_AND_VALUE(GetContents(path_via_symlink)); - ASSERT_EQ(contents, "abc"); -} - -TEST_F(OpenTest, Fault) { - char* totally_not_null = nullptr; - ASSERT_THAT(open(totally_not_null, O_RDONLY), SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(OpenTest, AppendOnly) { - // First write some data to the fresh file. - const int64_t kBufSize = 1024; - std::vector<char> buf(kBufSize, 'a'); - - FileDescriptor fd0 = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR)); - - std::fill(buf.begin(), buf.end(), 'a'); - EXPECT_THAT(WriteFd(fd0.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - fd0.reset(); // Close the file early. - - // Next get two handles to the same file. We open two files because we want - // to make sure that appending is respected between them. - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_APPEND)); - EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_APPEND)); - EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - // Then try to write to the first fd and make sure the bytes are appended. - EXPECT_THAT(WriteFd(fd1.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Check that the size of the file is correct and that the offset has been - // incremented to that size. - struct stat s0; - EXPECT_THAT(fstat(fd1.get(), &s0), SyscallSucceeds()); - EXPECT_EQ(s0.st_size, kBufSize * 2); - EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(kBufSize * 2)); - - // Then try to write to the second fd and make sure the bytes are appended. - EXPECT_THAT(WriteFd(fd2.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Check that the size of the file is correct and that the offset has been - // incremented to that size. - struct stat s1; - EXPECT_THAT(fstat(fd2.get(), &s1), SyscallSucceeds()); - EXPECT_EQ(s1.st_size, kBufSize * 3); - EXPECT_THAT(lseek(fd2.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(kBufSize * 3)); -} - -TEST_F(OpenTest, AppendConcurrentWrite) { - constexpr int kThreadCount = 5; - constexpr int kBytesPerThread = 10000; - std::unique_ptr<ScopedThread> threads[kThreadCount]; - - // In case of the uncached policy, we expect that a file system can be changed - // externally, so we create a new inode each time when we open a file and we - // can't guarantee that writes to files with O_APPEND will work correctly. - SKIP_IF(getenv("GVISOR_GOFER_UNCACHED")); - - EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds()); - - std::string filename = test_file_name_; - DisableSave ds; // Too many syscalls. - // Start kThreadCount threads which will write concurrently into the same - // file. - for (int i = 0; i < kThreadCount; i++) { - threads[i] = absl::make_unique<ScopedThread>([filename]() { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR | O_APPEND)); - - for (int j = 0; j < kBytesPerThread; j++) { - EXPECT_THAT(WriteFd(fd.get(), &j, 1), SyscallSucceedsWithValue(1)); - } - }); - } - for (int i = 0; i < kThreadCount; i++) { - threads[i]->Join(); - } - - // Check that the size of the file is correct. - struct stat st; - EXPECT_THAT(stat(test_file_name_.c_str(), &st), SyscallSucceeds()); - EXPECT_EQ(st.st_size, kThreadCount * kBytesPerThread); -} - -TEST_F(OpenTest, Truncate) { - { - // First write some data to the new file and close it. - FileDescriptor fd0 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY)); - std::vector<char> orig(10, 'a'); - EXPECT_THAT(WriteFd(fd0.get(), orig.data(), orig.size()), - SyscallSucceedsWithValue(orig.size())); - } - - // Then open with truncate and verify that offset is set to 0. - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR | O_TRUNC)); - EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - // Then write less data to the file and ensure the old content is gone. - std::vector<char> want(5, 'b'); - EXPECT_THAT(WriteFd(fd1.get(), want.data(), want.size()), - SyscallSucceedsWithValue(want.size())); - - struct stat stat; - EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds()); - EXPECT_EQ(stat.st_size, want.size()); - EXPECT_THAT(lseek(fd1.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(want.size())); - - // Read the data and ensure only the latest write is in the file. - std::vector<char> got(want.size() + 1, 'c'); - ASSERT_THAT(pread(fd1.get(), got.data(), got.size(), 0), - SyscallSucceedsWithValue(want.size())); - EXPECT_EQ(memcmp(want.data(), got.data(), want.size()), 0) - << "rbuf=" << got.data(); - EXPECT_EQ(got.back(), 'c'); // Last byte should not have been modified. -} - -TEST_F(OpenTest, NameTooLong) { - char buf[4097] = {}; - memset(buf, 'a', 4097); - EXPECT_THAT(open(buf, O_RDONLY), SyscallFailsWithErrno(ENAMETOOLONG)); -} - -TEST_F(OpenTest, DotsFromRoot) { - const FileDescriptor rootfd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/", O_RDONLY | O_DIRECTORY)); - const FileDescriptor other_rootfd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAt(rootfd.get(), "..", O_RDONLY)); -} - -TEST_F(OpenTest, DirectoryWritableFails) { - ASSERT_THAT(open(GetAbsoluteTestTmpdir().c_str(), O_RDWR), - SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(OpenTest, FileNotDirectory) { - // Create a file and try to open it with O_DIRECTORY. - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - ASSERT_THAT(open(file.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallFailsWithErrno(ENOTDIR)); -} - -TEST_F(OpenTest, SymlinkDirectory) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - std::string link = NewTempAbsPath(); - ASSERT_THAT(symlink(dir.path().c_str(), link.c_str()), SyscallSucceeds()); - ASSERT_NO_ERRNO(Open(link, O_RDONLY | O_DIRECTORY)); -} - -TEST_F(OpenTest, Null) { - char c = '\0'; - ASSERT_THAT(open(&c, O_RDONLY), SyscallFailsWithErrno(ENOENT)); -} - -// NOTE(b/119785738): While the man pages specify that this behavior should be -// undefined, Linux truncates the file on opening read only if we have write -// permission, so we will too. -TEST_F(OpenTest, CanTruncateReadOnly) { - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY | O_TRUNC)); - - struct stat stat; - EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds()); - EXPECT_EQ(stat.st_size, 0); -} - -// If we don't have read permission on the file, opening with -// O_TRUNC should fail. -TEST_F(OpenTest, CanTruncateReadOnlyNoWritePermission_NoRandomSave) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - const DisableSave ds; // Permissions are dropped. - ASSERT_THAT(chmod(test_file_name_.c_str(), S_IRUSR | S_IRGRP), - SyscallSucceeds()); - - ASSERT_THAT(open(test_file_name_.c_str(), O_RDONLY | O_TRUNC), - SyscallFailsWithErrno(EACCES)); - - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - - struct stat stat; - EXPECT_THAT(fstat(fd1.get(), &stat), SyscallSucceeds()); - EXPECT_EQ(stat.st_size, test_data_.size()); -} - -// If we don't have read permission but have write permission, opening O_WRONLY -// and O_TRUNC should succeed. -TEST_F(OpenTest, CanTruncateWriteOnlyNoReadPermission_NoRandomSave) { - const DisableSave ds; // Permissions are dropped. - - EXPECT_THAT(fchmod(test_file_fd_.get(), S_IWUSR | S_IWGRP), - SyscallSucceeds()); - - const FileDescriptor fd1 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_WRONLY | O_TRUNC)); - - EXPECT_THAT(fchmod(test_file_fd_.get(), S_IRUSR | S_IRGRP), - SyscallSucceeds()); - - const FileDescriptor fd2 = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - - struct stat stat; - EXPECT_THAT(fstat(fd2.get(), &stat), SyscallSucceeds()); - EXPECT_EQ(stat.st_size, 0); -} - -TEST_F(OpenTest, CanTruncateWithStrangePermissions) { - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - const DisableSave ds; // Permissions are dropped. - std::string path = NewTempAbsPath(); - // Create a file without user permissions. - EXPECT_NO_ERRNO(Open(path, O_CREAT | O_TRUNC | O_WRONLY, 055)); - - // Cannot open file because we are owner and have no permissions set. - EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); - - // We *can* chmod the file, because we are the owner. - EXPECT_THAT(chmod(path.c_str(), 0755), SyscallSucceeds()); - - // Now we can open the file again. - EXPECT_NO_ERRNO(Open(path, O_RDWR)); -} - -TEST_F(OpenTest, OpenNonDirectoryWithTrailingSlash) { - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string bad_path = file.path() + "/"; - EXPECT_THAT(open(bad_path.c_str(), O_RDONLY), SyscallFailsWithErrno(ENOTDIR)); -} - -TEST_F(OpenTest, OpenWithStrangeFlags) { - // VFS1 incorrectly allows read/write operations on such file descriptors. - SKIP_IF(IsRunningWithVFS1()); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY | O_RDWR)); - EXPECT_THAT(write(fd.get(), "x", 1), SyscallFailsWithErrno(EBADF)); - char c; - EXPECT_THAT(read(fd.get(), &c, 1), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(OpenTest, OpenWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - const DisableSave ds; // Permissions are dropped. - std::string path = NewTempAbsPath(); - - // Create a file without user permissions. - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 055)); - - // Cannot open file as read only because we are owner and have no permissions - // set. - EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); - - // Can open file with O_PATH because don't need permissions on the object when - // opening with O_PATH. - ASSERT_NO_ERRNO(Open(path, O_PATH)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/open_create.cc b/test/syscalls/linux/open_create.cc deleted file mode 100644 index 46f41de50..000000000 --- a/test/syscalls/linux/open_create.cc +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/temp_umask.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { -TEST(CreateTest, TmpFile) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_NO_ERRNO(Open(JoinPath(dir.path(), "a"), O_RDWR | O_CREAT, 0666)); -} - -TEST(CreateTest, ExistingFile) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto path = JoinPath(dir.path(), "ExistingFile"); - EXPECT_NO_ERRNO(Open(path, O_RDWR | O_CREAT, 0666)); - EXPECT_NO_ERRNO(Open(path, O_RDWR | O_CREAT, 0666)); -} - -TEST(CreateTest, CreateAtFile) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dirfd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY, 0666)); - int fd; - EXPECT_THAT(fd = openat(dirfd.get(), "CreateAtFile", O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(CreateTest, HonorsUmask_NoRandomSave) { - const DisableSave ds; // file cannot be re-opened as writable. - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempUmask mask(0222); - auto fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(dir.path(), "UmaskedFile"), O_RDWR | O_CREAT, 0666)); - struct stat statbuf; - ASSERT_THAT(fstat(fd.get(), &statbuf), SyscallSucceeds()); - EXPECT_EQ(0444, statbuf.st_mode & 0777); -} - -TEST(CreateTest, CreateExclusively) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto path = JoinPath(dir.path(), "foo"); - EXPECT_NO_ERRNO(Open(path, O_CREAT | O_RDWR, 0644)); - EXPECT_THAT(open(path.c_str(), O_CREAT | O_EXCL | O_RDWR, 0644), - SyscallFailsWithErrno(EEXIST)); -} - -TEST(CreateTest, CreatWithOTrunc) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT(open(dir.path().c_str(), O_CREAT | O_TRUNC, 0666), - SyscallFailsWithErrno(EISDIR)); -} - -TEST(CreateTest, CreatDirWithOTruncAndReadOnly) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT(open(dir.path().c_str(), O_CREAT | O_TRUNC | O_RDONLY, 0666), - SyscallFailsWithErrno(EISDIR)); -} - -TEST(CreateTest, CreatFileWithOTruncAndReadOnly) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto path = JoinPath(dir.path(), "foo"); - ASSERT_NO_ERRNO(Open(path, O_RDWR | O_CREAT, 0666)); - ASSERT_NO_ERRNO(Open(path, O_CREAT | O_TRUNC | O_RDONLY, 0666)); -} - -TEST(CreateTest, CreateFailsOnDirWithoutWritePerms) { - // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to - // always override directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - auto parent = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555)); - auto file = JoinPath(parent.path(), "foo"); - ASSERT_THAT(open(file.c_str(), O_CREAT | O_RDWR, 0644), - SyscallFailsWithErrno(EACCES)); -} - -// A file originally created RW, but opened RO can later be opened RW. -// Regression test for b/65385065. -TEST(CreateTest, OpenCreateROThenRW) { - TempPath file(NewTempAbsPath()); - - // Create a RW file, but only open it RO. - FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE( - Open(file.path(), O_CREAT | O_EXCL | O_RDONLY, 0644)); - - // Now get a RW FD. - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - // fd1 is not writable, but fd2 is. - char c = 'a'; - EXPECT_THAT(WriteFd(fd1.get(), &c, 1), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(WriteFd(fd2.get(), &c, 1), SyscallSucceedsWithValue(1)); -} - -TEST(CreateTest, ChmodReadToWriteBetweenOpens_NoRandomSave) { - // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to - // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be - // cleared for the same reason. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400)); - - const FileDescriptor rfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - // Cannot restore after making permissions more restrictive. - const DisableSave ds; - ASSERT_THAT(fchmod(rfd.get(), 0200), SyscallSucceeds()); - - EXPECT_THAT(open(file.path().c_str(), O_RDONLY), - SyscallFailsWithErrno(EACCES)); - - const FileDescriptor wfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - - char c = 'x'; - EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - c = 0; - EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(c, 'x'); -} - -TEST(CreateTest, ChmodWriteToReadBetweenOpens_NoRandomSave) { - // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to - // override file read/write permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0200)); - - const FileDescriptor wfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - - // Cannot restore after making permissions more restrictive. - const DisableSave ds; - ASSERT_THAT(fchmod(wfd.get(), 0400), SyscallSucceeds()); - - EXPECT_THAT(open(file.path().c_str(), O_WRONLY), - SyscallFailsWithErrno(EACCES)); - - const FileDescriptor rfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - char c = 'x'; - EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - c = 0; - EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(c, 'x'); -} - -TEST(CreateTest, CreateWithReadFlagNotAllowedByMode_NoRandomSave) { - // The only time we can open a file with flags forbidden by its permissions - // is when we are creating the file. We cannot re-open with the same flags, - // so we cannot restore an fd obtained from such an operation. - const DisableSave ds; - - // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to - // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be - // cleared for the same reason. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - // Create and open a file with read flag but without read permissions. - const std::string path = NewTempAbsPath(); - const FileDescriptor rfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_RDONLY, 0222)); - - EXPECT_THAT(open(path.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); - const FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY)); - - char c = 'x'; - EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - c = 0; - EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(c, 'x'); -} - -TEST(CreateTest, CreateWithWriteFlagNotAllowedByMode_NoRandomSave) { - // The only time we can open a file with flags forbidden by its permissions - // is when we are creating the file. We cannot re-open with the same flags, - // so we cannot restore an fd obtained from such an operation. - const DisableSave ds; - - // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to - // override file read/write permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - // Create and open a file with write flag but without write permissions. - const std::string path = NewTempAbsPath(); - const FileDescriptor wfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_CREAT | O_WRONLY, 0444)); - - EXPECT_THAT(open(path.c_str(), O_WRONLY), SyscallFailsWithErrno(EACCES)); - const FileDescriptor rfd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDONLY)); - - char c = 'x'; - EXPECT_THAT(write(wfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - c = 0; - EXPECT_THAT(read(rfd.get(), &c, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ(c, 'x'); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc deleted file mode 100644 index 861617ff7..000000000 --- a/test/syscalls/linux/packet_socket.cc +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <ifaddrs.h> -#include <linux/capability.h> -#include <linux/if_arp.h> -#include <linux/if_packet.h> -#include <net/ethernet.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/udp.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/base/internal/endian.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Some of these tests involve sending packets via AF_PACKET sockets and the -// loopback interface. Because AF_PACKET circumvents so much of the networking -// stack, Linux sees these packets as "martian", i.e. they claim to be to/from -// localhost but don't have the usual associated data. Thus Linux drops them by -// default. You can see where this happens by following the code at: -// -// - net/ipv4/ip_input.c:ip_rcv_finish, which calls -// - net/ipv4/route.c:ip_route_input_noref, which calls -// - net/ipv4/route.c:ip_route_input_slow, which finds and drops martian -// packets. -// -// To tell Linux not to drop these packets, you need to tell it to accept our -// funny packets (which are completely valid and correct, but lack associated -// in-kernel data because we use AF_PACKET): -// -// echo 1 >> /proc/sys/net/ipv4/conf/lo/accept_local -// echo 1 >> /proc/sys/net/ipv4/conf/lo/route_localnet -// -// These tests require CAP_NET_RAW to run. - -// TODO(gvisor.dev/issue/173): gVisor support. - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::AnyOf; -using ::testing::Eq; - -constexpr char kMessage[] = "soweoneul malhaebwa"; -constexpr in_port_t kPort = 0x409c; // htons(40000) - -// -// "Cooked" tests. Cooked AF_PACKET sockets do not contain link layer -// headers, and provide link layer destination/source information via a -// returned struct sockaddr_ll. -// - -// Send kMessage via sock to loopback -void SendUDPMessage(int sock) { - struct sockaddr_in dest = {}; - dest.sin_port = kPort; - dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - dest.sin_family = AF_INET; - EXPECT_THAT(sendto(sock, kMessage, sizeof(kMessage), 0, - reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)), - SyscallSucceedsWithValue(sizeof(kMessage))); -} - -// Send an IP packet and make sure ETH_P_<something else> doesn't pick it up. -TEST(BasicCookedPacketTest, WrongType) { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, ETH_P_PUP), - SyscallFailsWithErrno(EPERM)); - GTEST_SKIP(); - } - - FileDescriptor sock = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_PUP))); - - // Let's use a simple IP payload: a UDP datagram. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - SendUDPMessage(udp_sock.get()); - - // Wait and make sure the socket never becomes readable. - struct pollfd pfd = {}; - pfd.fd = sock.get(); - pfd.events = POLLIN; - EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0)); -} - -// Tests for "cooked" (SOCK_DGRAM) packet(7) sockets. -class CookedPacketTest : public ::testing::TestWithParam<int> { - protected: - // Creates a socket to be used in tests. - void SetUp() override; - - // Closes the socket created by SetUp(). - void TearDown() override; - - // Gets the device index of the loopback device. - int GetLoopbackIndex(); - - // The socket used for both reading and writing. - int socket_; -}; - -void CookedPacketTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())), - SyscallFailsWithErrno(EPERM)); - GTEST_SKIP(); - } - - if (!IsRunningOnGvisor()) { - FileDescriptor acceptLocal = ASSERT_NO_ERRNO_AND_VALUE( - Open("/proc/sys/net/ipv4/conf/lo/accept_local", O_RDONLY)); - FileDescriptor routeLocalnet = ASSERT_NO_ERRNO_AND_VALUE( - Open("/proc/sys/net/ipv4/conf/lo/route_localnet", O_RDONLY)); - char enabled; - ASSERT_THAT(read(acceptLocal.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - ASSERT_EQ(enabled, '1'); - ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - ASSERT_EQ(enabled, '1'); - } - - ASSERT_THAT(socket_ = socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())), - SyscallSucceeds()); -} - -void CookedPacketTest::TearDown() { - // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - EXPECT_THAT(close(socket_), SyscallSucceeds()); - } -} - -int CookedPacketTest::GetLoopbackIndex() { - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - return ifr.ifr_ifindex; -} - -// Receive and verify the message via packet socket on interface. -void ReceiveMessage(int sock, int ifindex) { - // Wait for the socket to become readable. - struct pollfd pfd = {}; - pfd.fd = sock; - pfd.events = POLLIN; - EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1)); - - // Read and verify the data. - constexpr size_t packet_size = - sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage); - char buf[64]; - struct sockaddr_ll src = {}; - socklen_t src_len = sizeof(src); - ASSERT_THAT(recvfrom(sock, buf, sizeof(buf), 0, - reinterpret_cast<struct sockaddr*>(&src), &src_len), - SyscallSucceedsWithValue(packet_size)); - - // sockaddr_ll ends with an 8 byte physical address field, but ethernet - // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2 - // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns - // sizeof(sockaddr_ll). - ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2))); - - // TODO(gvisor.dev/issue/173): Verify protocol once we return it. - // Verify the source address. - EXPECT_EQ(src.sll_family, AF_PACKET); - EXPECT_EQ(src.sll_ifindex, ifindex); - EXPECT_EQ(src.sll_halen, ETH_ALEN); - EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP); - // This came from the loopback device, so the address is all 0s. - for (int i = 0; i < src.sll_halen; i++) { - EXPECT_EQ(src.sll_addr[i], 0); - } - - // Verify the IP header. We memcpy to deal with pointer aligment. - struct iphdr ip = {}; - memcpy(&ip, buf, sizeof(ip)); - EXPECT_EQ(ip.ihl, 5); - EXPECT_EQ(ip.version, 4); - EXPECT_EQ(ip.tot_len, htons(packet_size)); - EXPECT_EQ(ip.protocol, IPPROTO_UDP); - EXPECT_EQ(ip.daddr, htonl(INADDR_LOOPBACK)); - EXPECT_EQ(ip.saddr, htonl(INADDR_LOOPBACK)); - - // Verify the UDP header. We memcpy to deal with pointer aligment. - struct udphdr udp = {}; - memcpy(&udp, buf + sizeof(iphdr), sizeof(udp)); - EXPECT_EQ(udp.dest, kPort); - EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage))); - - // Verify the payload. - char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr)); - EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0); -} - -// Receive via a packet socket. -TEST_P(CookedPacketTest, Receive) { - // Let's use a simple IP payload: a UDP datagram. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - SendUDPMessage(udp_sock.get()); - - // Receive and verify the data. - int loopback_index = GetLoopbackIndex(); - ReceiveMessage(socket_, loopback_index); -} - -// Send via a packet socket. -TEST_P(CookedPacketTest, Send) { - // TODO(gvisor.dev/issue/173): Remove once we support packet socket writing. - SKIP_IF(IsRunningOnGvisor()); - - // Let's send a UDP packet and receive it using a regular UDP socket. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - struct sockaddr_in bind_addr = {}; - bind_addr.sin_family = AF_INET; - bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - bind_addr.sin_port = kPort; - ASSERT_THAT( - bind(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallSucceeds()); - - // Set up the destination physical address. - struct sockaddr_ll dest = {}; - dest.sll_family = AF_PACKET; - dest.sll_halen = ETH_ALEN; - dest.sll_ifindex = GetLoopbackIndex(); - dest.sll_protocol = htons(ETH_P_IP); - // We're sending to the loopback device, so the address is all 0s. - memset(dest.sll_addr, 0x00, ETH_ALEN); - - // Set up the IP header. - struct iphdr iphdr = {0}; - iphdr.ihl = 5; - iphdr.version = 4; - iphdr.tos = 0; - iphdr.tot_len = - htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage)); - // Get a pseudo-random ID. If we clash with an in-use ID the test will fail, - // but we have no way of getting an ID we know to be good. - srand(*reinterpret_cast<unsigned int*>(&iphdr)); - iphdr.id = rand(); - // Linux sets this bit ("do not fragment") for small packets. - iphdr.frag_off = 1 << 6; - iphdr.ttl = 64; - iphdr.protocol = IPPROTO_UDP; - iphdr.daddr = htonl(INADDR_LOOPBACK); - iphdr.saddr = htonl(INADDR_LOOPBACK); - iphdr.check = IPChecksum(iphdr); - - // Set up the UDP header. - struct udphdr udphdr = {}; - udphdr.source = kPort; - udphdr.dest = kPort; - udphdr.len = htons(sizeof(udphdr) + sizeof(kMessage)); - udphdr.check = UDPChecksum(iphdr, udphdr, kMessage, sizeof(kMessage)); - - // Copy both headers and the payload into our packet buffer. - char send_buf[sizeof(iphdr) + sizeof(udphdr) + sizeof(kMessage)]; - memcpy(send_buf, &iphdr, sizeof(iphdr)); - memcpy(send_buf + sizeof(iphdr), &udphdr, sizeof(udphdr)); - memcpy(send_buf + sizeof(iphdr) + sizeof(udphdr), kMessage, sizeof(kMessage)); - - // Send it. - ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0, - reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Wait for the packet to become available on both sockets. - struct pollfd pfd = {}; - pfd.fd = udp_sock.get(); - pfd.events = POLLIN; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1)); - pfd.fd = socket_; - pfd.events = POLLIN; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1)); - - // Receive on the packet socket. - char recv_buf[sizeof(send_buf)]; - ASSERT_THAT(recv(socket_, recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - ASSERT_EQ(memcmp(recv_buf, send_buf, sizeof(send_buf)), 0); - - // Receive on the UDP socket. - struct sockaddr_in src; - socklen_t src_len = sizeof(src); - ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT, - reinterpret_cast<struct sockaddr*>(&src), &src_len), - SyscallSucceedsWithValue(sizeof(kMessage))); - // Check src and payload. - EXPECT_EQ(strncmp(recv_buf, kMessage, sizeof(kMessage)), 0); - EXPECT_EQ(src.sin_family, AF_INET); - EXPECT_EQ(src.sin_port, kPort); - EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); -} - -// Bind and receive via packet socket. -TEST_P(CookedPacketTest, BindReceive) { - struct sockaddr_ll bind_addr = {}; - bind_addr.sll_family = AF_PACKET; - bind_addr.sll_protocol = htons(GetParam()); - bind_addr.sll_ifindex = GetLoopbackIndex(); - - ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallSucceeds()); - - // Let's use a simple IP payload: a UDP datagram. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - SendUDPMessage(udp_sock.get()); - - // Receive and verify the data. - ReceiveMessage(socket_, bind_addr.sll_ifindex); -} - -// Double Bind socket. -TEST_P(CookedPacketTest, DoubleBindSucceeds) { - struct sockaddr_ll bind_addr = {}; - bind_addr.sll_family = AF_PACKET; - bind_addr.sll_protocol = htons(GetParam()); - bind_addr.sll_ifindex = GetLoopbackIndex(); - - ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallSucceeds()); - - // Binding socket again should fail. - ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - // Linux 4.09 returns EINVAL here, but some time before 4.19 it - // switched to EADDRINUSE. - SyscallSucceeds()); -} - -// Bind and verify we do not receive data on interface which is not bound -TEST_P(CookedPacketTest, BindDrop) { - // Let's use a simple IP payload: a UDP datagram. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - struct ifaddrs* if_addr_list = nullptr; - auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); }); - - ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds()); - - // Get interface other than loopback. - struct ifreq ifr = {}; - for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) { - if (strcmp(i->ifa_name, "lo") != 0) { - strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name)); - break; - } - } - - // Skip if no interface is available other than loopback. - if (strlen(ifr.ifr_name) == 0) { - GTEST_SKIP(); - } - - // Get interface index. - EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - - // Bind to packet socket requires only family, protocol and ifindex. - struct sockaddr_ll bind_addr = {}; - bind_addr.sll_family = AF_PACKET; - bind_addr.sll_protocol = htons(GetParam()); - bind_addr.sll_ifindex = ifr.ifr_ifindex; - - ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallSucceeds()); - - // Send to loopback interface. - struct sockaddr_in dest = {}; - dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - dest.sin_family = AF_INET; - dest.sin_port = kPort; - EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0, - reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)), - SyscallSucceedsWithValue(sizeof(kMessage))); - - // Wait and make sure the socket never receives any data. - struct pollfd pfd = {}; - pfd.fd = socket_; - pfd.events = POLLIN; - EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0)); -} - -// Verify that we receive outbound packets. This test requires at least one -// non loopback interface so that we can actually capture an outgoing packet. -TEST_P(CookedPacketTest, ReceiveOutbound) { - // Only ETH_P_ALL sockets can receive outbound packets on linux. - SKIP_IF(GetParam() != ETH_P_ALL); - - // Let's use a simple IP payload: a UDP datagram. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - struct ifaddrs* if_addr_list = nullptr; - auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); }); - - ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds()); - - // Get interface other than loopback. - struct ifreq ifr = {}; - for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) { - if (strcmp(i->ifa_name, "lo") != 0) { - strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name)); - break; - } - } - - // Skip if no interface is available other than loopback. - if (strlen(ifr.ifr_name) == 0) { - GTEST_SKIP(); - } - - // Get interface index and name. - EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - int ifindex = ifr.ifr_ifindex; - - constexpr int kMACSize = 6; - char hwaddr[kMACSize]; - // Get interface address. - ASSERT_THAT(ioctl(socket_, SIOCGIFHWADDR, &ifr), SyscallSucceeds()); - ASSERT_THAT(ifr.ifr_hwaddr.sa_family, - AnyOf(Eq(ARPHRD_NONE), Eq(ARPHRD_ETHER))); - memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, kMACSize); - - // Just send it to the google dns server 8.8.8.8. It's UDP we don't care - // if it actually gets to the DNS Server we just want to see that we receive - // it on our AF_PACKET socket. - // - // NOTE: We just want to pick an IP that is non-local to avoid having to - // handle ARP as this should cause the UDP packet to be sent to the default - // gateway configured for the system under test. Otherwise the only packet we - // will see is the ARP query unless we picked an IP which will actually - // resolve. The test is a bit brittle but this was the best compromise for - // now. - struct sockaddr_in dest = {}; - ASSERT_EQ(inet_pton(AF_INET, "8.8.8.8", &dest.sin_addr.s_addr), 1); - dest.sin_family = AF_INET; - dest.sin_port = kPort; - EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0, - reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)), - SyscallSucceedsWithValue(sizeof(kMessage))); - - // Wait and make sure the socket receives the data. - struct pollfd pfd = {}; - pfd.fd = socket_; - pfd.events = POLLIN; - EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(1)); - - // Now read and check that the packet is the one we just sent. - // Read and verify the data. - constexpr size_t packet_size = - sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage); - char buf[64]; - struct sockaddr_ll src = {}; - socklen_t src_len = sizeof(src); - ASSERT_THAT(recvfrom(socket_, buf, sizeof(buf), 0, - reinterpret_cast<struct sockaddr*>(&src), &src_len), - SyscallSucceedsWithValue(packet_size)); - - // sockaddr_ll ends with an 8 byte physical address field, but ethernet - // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2 - // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns - // sizeof(sockaddr_ll). - ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2))); - - // Verify the source address. - EXPECT_EQ(src.sll_family, AF_PACKET); - EXPECT_EQ(src.sll_ifindex, ifindex); - EXPECT_EQ(src.sll_halen, ETH_ALEN); - EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP); - EXPECT_EQ(src.sll_pkttype, PACKET_OUTGOING); - // Verify the link address of the interface matches that of the non - // non loopback interface address we stored above. - for (int i = 0; i < src.sll_halen; i++) { - EXPECT_EQ(src.sll_addr[i], hwaddr[i]); - } - - // Verify the IP header. - struct iphdr ip = {}; - memcpy(&ip, buf, sizeof(ip)); - EXPECT_EQ(ip.ihl, 5); - EXPECT_EQ(ip.version, 4); - EXPECT_EQ(ip.tot_len, htons(packet_size)); - EXPECT_EQ(ip.protocol, IPPROTO_UDP); - EXPECT_EQ(ip.daddr, dest.sin_addr.s_addr); - EXPECT_NE(ip.saddr, htonl(INADDR_LOOPBACK)); - - // Verify the UDP header. - struct udphdr udp = {}; - memcpy(&udp, buf + sizeof(iphdr), sizeof(udp)); - EXPECT_EQ(udp.dest, kPort); - EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage))); - - // Verify the payload. - char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr)); - EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0); -} - -// Bind with invalid address. -TEST_P(CookedPacketTest, BindFail) { - // Null address. - ASSERT_THAT( - bind(socket_, nullptr, sizeof(struct sockaddr)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EINVAL))); - - // Address of size 1. - uint8_t addr = 0; - ASSERT_THAT( - bind(socket_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(EINVAL)); -} - -INSTANTIATE_TEST_SUITE_P(AllInetTests, CookedPacketTest, - ::testing::Values(ETH_P_IP, ETH_P_ALL)); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc deleted file mode 100644 index d25be0e30..000000000 --- a/test/syscalls/linux/packet_socket_raw.cc +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <linux/capability.h> -#include <linux/filter.h> -#include <linux/if_arp.h> -#include <linux/if_packet.h> -#include <net/ethernet.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/udp.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/internal/endian.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Some of these tests involve sending packets via AF_PACKET sockets and the -// loopback interface. Because AF_PACKET circumvents so much of the networking -// stack, Linux sees these packets as "martian", i.e. they claim to be to/from -// localhost but don't have the usual associated data. Thus Linux drops them by -// default. You can see where this happens by following the code at: -// -// - net/ipv4/ip_input.c:ip_rcv_finish, which calls -// - net/ipv4/route.c:ip_route_input_noref, which calls -// - net/ipv4/route.c:ip_route_input_slow, which finds and drops martian -// packets. -// -// To tell Linux not to drop these packets, you need to tell it to accept our -// funny packets (which are completely valid and correct, but lack associated -// in-kernel data because we use AF_PACKET): -// -// echo 1 >> /proc/sys/net/ipv4/conf/lo/accept_local -// echo 1 >> /proc/sys/net/ipv4/conf/lo/route_localnet -// -// These tests require CAP_NET_RAW to run. - -// TODO(gvisor.dev/issue/173): gVisor support. - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::AnyOf; -using ::testing::Eq; - -constexpr char kMessage[] = "soweoneul malhaebwa"; -constexpr in_port_t kPort = 0x409c; // htons(40000) - -// Send kMessage via sock to loopback -void SendUDPMessage(int sock) { - struct sockaddr_in dest = {}; - dest.sin_port = kPort; - dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - dest.sin_family = AF_INET; - EXPECT_THAT(sendto(sock, kMessage, sizeof(kMessage), 0, - reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)), - SyscallSucceedsWithValue(sizeof(kMessage))); -} - -// -// Raw tests. Packets sent with raw AF_PACKET sockets always include link layer -// headers. -// - -// Tests for "raw" (SOCK_RAW) packet(7) sockets. -class RawPacketTest : public ::testing::TestWithParam<int> { - protected: - // Creates a socket to be used in tests. - void SetUp() override; - - // Closes the socket created by SetUp(). - void TearDown() override; - - // Gets the device index of the loopback device. - int GetLoopbackIndex(); - - // The socket used for both reading and writing. - int s_; -}; - -void RawPacketTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - ASSERT_THAT(socket(AF_PACKET, SOCK_RAW, htons(GetParam())), - SyscallFailsWithErrno(EPERM)); - GTEST_SKIP(); - } - - if (!IsRunningOnGvisor()) { - // Ensure that looped back packets aren't rejected by the kernel. - FileDescriptor acceptLocal = ASSERT_NO_ERRNO_AND_VALUE( - Open("/proc/sys/net/ipv4/conf/lo/accept_local", O_RDWR)); - FileDescriptor routeLocalnet = ASSERT_NO_ERRNO_AND_VALUE( - Open("/proc/sys/net/ipv4/conf/lo/route_localnet", O_RDWR)); - char enabled; - ASSERT_THAT(read(acceptLocal.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - if (enabled != '1') { - enabled = '1'; - ASSERT_THAT(lseek(acceptLocal.get(), 0, SEEK_SET), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(write(acceptLocal.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - ASSERT_THAT(lseek(acceptLocal.get(), 0, SEEK_SET), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(read(acceptLocal.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - ASSERT_EQ(enabled, '1'); - } - - ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - if (enabled != '1') { - enabled = '1'; - ASSERT_THAT(lseek(routeLocalnet.get(), 0, SEEK_SET), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(write(routeLocalnet.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - ASSERT_THAT(lseek(routeLocalnet.get(), 0, SEEK_SET), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1), - SyscallSucceedsWithValue(1)); - ASSERT_EQ(enabled, '1'); - } - } - - ASSERT_THAT(s_ = socket(AF_PACKET, SOCK_RAW, htons(GetParam())), - SyscallSucceeds()); -} - -void RawPacketTest::TearDown() { - // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - EXPECT_THAT(close(s_), SyscallSucceeds()); - } -} - -int RawPacketTest::GetLoopbackIndex() { - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - EXPECT_THAT(ioctl(s_, SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - return ifr.ifr_ifindex; -} - -// Receive via a packet socket. -TEST_P(RawPacketTest, Receive) { - // Let's use a simple IP payload: a UDP datagram. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - SendUDPMessage(udp_sock.get()); - - // Wait for the socket to become readable. - struct pollfd pfd = {}; - pfd.fd = s_; - pfd.events = POLLIN; - EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1)); - - // Read and verify the data. - constexpr size_t packet_size = sizeof(struct ethhdr) + sizeof(struct iphdr) + - sizeof(struct udphdr) + sizeof(kMessage); - char buf[64]; - struct sockaddr_ll src = {}; - socklen_t src_len = sizeof(src); - ASSERT_THAT(recvfrom(s_, buf, sizeof(buf), 0, - reinterpret_cast<struct sockaddr*>(&src), &src_len), - SyscallSucceedsWithValue(packet_size)); - // sockaddr_ll ends with an 8 byte physical address field, but ethernet - // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2 - // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns - // sizeof(sockaddr_ll). - ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2))); - - // TODO(gvisor.dev/issue/173): Verify protocol once we return it. - // Verify the source address. - EXPECT_EQ(src.sll_family, AF_PACKET); - EXPECT_EQ(src.sll_ifindex, GetLoopbackIndex()); - EXPECT_EQ(src.sll_halen, ETH_ALEN); - EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP); - // This came from the loopback device, so the address is all 0s. - for (int i = 0; i < src.sll_halen; i++) { - EXPECT_EQ(src.sll_addr[i], 0); - } - - // Verify the ethernet header. We memcpy to deal with pointer alignment. - struct ethhdr eth = {}; - memcpy(ð, buf, sizeof(eth)); - // The destination and source address should be 0, for loopback. - for (int i = 0; i < ETH_ALEN; i++) { - EXPECT_EQ(eth.h_dest[i], 0); - EXPECT_EQ(eth.h_source[i], 0); - } - EXPECT_EQ(eth.h_proto, htons(ETH_P_IP)); - - // Verify the IP header. We memcpy to deal with pointer aligment. - struct iphdr ip = {}; - memcpy(&ip, buf + sizeof(ethhdr), sizeof(ip)); - EXPECT_EQ(ip.ihl, 5); - EXPECT_EQ(ip.version, 4); - EXPECT_EQ(ip.tot_len, htons(packet_size - sizeof(eth))); - EXPECT_EQ(ip.protocol, IPPROTO_UDP); - EXPECT_EQ(ip.daddr, htonl(INADDR_LOOPBACK)); - EXPECT_EQ(ip.saddr, htonl(INADDR_LOOPBACK)); - - // Verify the UDP header. We memcpy to deal with pointer aligment. - struct udphdr udp = {}; - memcpy(&udp, buf + sizeof(eth) + sizeof(iphdr), sizeof(udp)); - EXPECT_EQ(udp.dest, kPort); - EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage))); - - // Verify the payload. - char* payload = reinterpret_cast<char*>(buf + sizeof(eth) + sizeof(iphdr) + - sizeof(udphdr)); - EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0); -} - -// Send via a packet socket. -TEST_P(RawPacketTest, Send) { - // TODO(gvisor.dev/issue/173): Remove once we support packet socket writing. - SKIP_IF(IsRunningOnGvisor()); - - // Let's send a UDP packet and receive it using a regular UDP socket. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - struct sockaddr_in bind_addr = {}; - bind_addr.sin_family = AF_INET; - bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - bind_addr.sin_port = kPort; - ASSERT_THAT( - bind(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallSucceeds()); - - // Set up the destination physical address. - struct sockaddr_ll dest = {}; - dest.sll_family = AF_PACKET; - dest.sll_halen = ETH_ALEN; - dest.sll_ifindex = GetLoopbackIndex(); - dest.sll_protocol = htons(ETH_P_IP); - // We're sending to the loopback device, so the address is all 0s. - memset(dest.sll_addr, 0x00, ETH_ALEN); - - // Set up the ethernet header. The kernel takes care of the footer. - // We're sending to and from hardware address 0 (loopback). - struct ethhdr eth = {}; - eth.h_proto = htons(ETH_P_IP); - - // Set up the IP header. - struct iphdr iphdr = {}; - iphdr.ihl = 5; - iphdr.version = 4; - iphdr.tos = 0; - iphdr.tot_len = - htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage)); - // Get a pseudo-random ID. If we clash with an in-use ID the test will fail, - // but we have no way of getting an ID we know to be good. - srand(*reinterpret_cast<unsigned int*>(&iphdr)); - iphdr.id = rand(); - // Linux sets this bit ("do not fragment") for small packets. - iphdr.frag_off = 1 << 6; - iphdr.ttl = 64; - iphdr.protocol = IPPROTO_UDP; - iphdr.daddr = htonl(INADDR_LOOPBACK); - iphdr.saddr = htonl(INADDR_LOOPBACK); - iphdr.check = IPChecksum(iphdr); - - // Set up the UDP header. - struct udphdr udphdr = {}; - udphdr.source = kPort; - udphdr.dest = kPort; - udphdr.len = htons(sizeof(udphdr) + sizeof(kMessage)); - udphdr.check = UDPChecksum(iphdr, udphdr, kMessage, sizeof(kMessage)); - - // Copy both headers and the payload into our packet buffer. - char - send_buf[sizeof(eth) + sizeof(iphdr) + sizeof(udphdr) + sizeof(kMessage)]; - memcpy(send_buf, ð, sizeof(eth)); - memcpy(send_buf + sizeof(ethhdr), &iphdr, sizeof(iphdr)); - memcpy(send_buf + sizeof(ethhdr) + sizeof(iphdr), &udphdr, sizeof(udphdr)); - memcpy(send_buf + sizeof(ethhdr) + sizeof(iphdr) + sizeof(udphdr), kMessage, - sizeof(kMessage)); - - // Send it. - ASSERT_THAT(sendto(s_, send_buf, sizeof(send_buf), 0, - reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Wait for the packet to become available on both sockets. - struct pollfd pfd = {}; - pfd.fd = udp_sock.get(); - pfd.events = POLLIN; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1)); - pfd.fd = s_; - pfd.events = POLLIN; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1)); - - // Receive on the packet socket. - char recv_buf[sizeof(send_buf)]; - ASSERT_THAT(recv(s_, recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - ASSERT_EQ(memcmp(recv_buf, send_buf, sizeof(send_buf)), 0); - - // Receive on the UDP socket. - struct sockaddr_in src; - socklen_t src_len = sizeof(src); - ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT, - reinterpret_cast<struct sockaddr*>(&src), &src_len), - SyscallSucceedsWithValue(sizeof(kMessage))); - // Check src and payload. - EXPECT_EQ(strncmp(recv_buf, kMessage, sizeof(kMessage)), 0); - EXPECT_EQ(src.sin_family, AF_INET); - EXPECT_EQ(src.sin_port, kPort); - EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK)); -} - -// Check that setting SO_RCVBUF below min is clamped to the minimum -// receive buffer size. -TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover minimum receive buf size by trying to set it to zero. - // See: - // https://github.com/torvalds/linux/blob/a5dc8300df75e8b8384b4c82225f1e4a0b4d9b55/net/core/sock.c#L820 - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - - // Linux doubles the value so let's use a value that when doubled will still - // be smaller than min. - int below_min = min / 2 - 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &below_min, sizeof(below_min)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - - ASSERT_EQ(min, val); -} - -// Check that setting SO_RCVBUF above max is clamped to the maximum -// receive buffer size. -TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover max buf size by trying to set the largest possible buffer size. - constexpr int kRcvBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &max, &max_len), - SyscallSucceeds()); - - int above_max = max + 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &above_max, sizeof(above_max)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(max, val); -} - -// Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored. -TEST_P(RawPacketTest, SetSocketRecvBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int max = 0; - int min = 0; - { - // Discover max buf size by trying to set a really large buffer size. - constexpr int kRcvBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by trying to set a zero size receive buffer - // size. - // See: - // https://github.com/torvalds/linux/blob/a5dc8300df75e8b8384b4c82225f1e4a0b4d9b55/net/core/sock.c#L820 - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &quarter_sz, sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - - // Linux doubles the value set by SO_SNDBUF/SO_RCVBUF. - // TODO(gvisor.dev/issue/2926): Remove when Netstack matches linux behavior. - if (!IsRunningOnGvisor()) { - quarter_sz *= 2; - } - ASSERT_EQ(quarter_sz, val); -} - -// Check that setting SO_SNDBUF below min is clamped to the minimum -// receive buffer size. -TEST_P(RawPacketTest, SetSocketSendBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover minimum buffer size by trying to set it to zero. - constexpr int kSndBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - - // Linux doubles the value so let's use a value that when doubled will still - // be smaller than min. - int below_min = min / 2 - 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &below_min, sizeof(below_min)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - ASSERT_EQ(min, val); -} - -// Check that setting SO_SNDBUF above max is clamped to the maximum -// send buffer size. -TEST_P(RawPacketTest, SetSocketSendBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover maximum buffer size by trying to set it to a large value. - constexpr int kSndBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - int max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - - int above_max = max + 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &above_max, sizeof(above_max)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(max, val); -} - -// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored. -TEST_P(RawPacketTest, SetSocketSendBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int max = 0; - int min = 0; - { - // Discover maximum buffer size by trying to set it to a large value. - constexpr int kSndBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by trying to set it to zero. - constexpr int kSndBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &quarter_sz, sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - quarter_sz *= 2; - ASSERT_EQ(quarter_sz, val); -} - -TEST_P(RawPacketTest, GetSocketError) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(val, 0); -} - -TEST_P(RawPacketTest, GetSocketErrorBind) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - { - // Bind to the loopback device. - struct sockaddr_ll bind_addr = {}; - bind_addr.sll_family = AF_PACKET; - bind_addr.sll_protocol = htons(GetParam()); - bind_addr.sll_ifindex = GetLoopbackIndex(); - - ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallSucceeds()); - - // SO_ERROR should return no errors. - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(val, 0); - } - - { - // Now try binding to an invalid interface. - struct sockaddr_ll bind_addr = {}; - bind_addr.sll_family = AF_PACKET; - bind_addr.sll_protocol = htons(GetParam()); - bind_addr.sll_ifindex = 0xffff; // Just pick a really large number. - - // Binding should fail with EINVAL - ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallFailsWithErrno(ENODEV)); - - // SO_ERROR does not return error when the device is invalid. - // On Linux there is just one odd ball condition where this can return - // an error where the device was valid and then removed or disabled - // between the first check for index and the actual registration of - // the packet endpoint. On Netstack this is not possible as the stack - // global mutex is held during registration and check. - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ERROR, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(val, 0); - } -} - -TEST_P(RawPacketTest, SetSocketDetachFilterNoInstalledFilter) { - // TODO(gvisor.dev/2746): Support SO_ATTACH_FILTER/SO_DETACH_FILTER. - // - // gVisor returns no error on SO_DETACH_FILTER even if there is no filter - // attached unlike linux which does return ENOENT in such cases. This is - // because gVisor doesn't support SO_ATTACH_FILTER and just silently returns - // success. - if (IsRunningOnGvisor()) { - constexpr int val = 0; - ASSERT_THAT(setsockopt(s_, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallSucceeds()); - return; - } - constexpr int val = 0; - ASSERT_THAT(setsockopt(s_, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallFailsWithErrno(ENOENT)); -} - -TEST_P(RawPacketTest, GetSocketDetachFilter) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_DETACH_FILTER, &val, &val_len), - SyscallFailsWithErrno(ENOPROTOOPT)); -} - -TEST_P(RawPacketTest, SetAndGetSocketLinger) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int level = SOL_SOCKET; - int type = SO_LINGER; - - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = 5; - ASSERT_THAT(setsockopt(s_, level, type, &sl, sizeof(sl)), - SyscallSucceedsWithValue(0)); - - struct linger got_linger = {}; - socklen_t length = sizeof(sl); - ASSERT_THAT(getsockopt(s_, level, type, &got_linger, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, length)); -} - -TEST_P(RawPacketTest, GetSocketAcceptConn) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); -} -INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest, - ::testing::Values(ETH_P_IP, ETH_P_ALL)); - -class RawPacketMsgSizeTest : public ::testing::TestWithParam<TestAddress> {}; - -TEST_P(RawPacketMsgSizeTest, SendTooLong) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - TestAddress addr = GetParam().WithPort(kPort); - - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(addr.family(), SOCK_RAW, IPPROTO_UDP)); - - ASSERT_THAT( - connect(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - const char buf[65536] = {}; - ASSERT_THAT(send(udp_sock.get(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EMSGSIZE)); -} - -TEST_P(RawPacketMsgSizeTest, SpliceTooLong) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - const char buf[65536] = {}; - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - ASSERT_THAT(write(fds[1], buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - TestAddress addr = GetParam().WithPort(kPort); - - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(addr.family(), SOCK_RAW, IPPROTO_UDP)); - - ASSERT_THAT( - connect(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - ssize_t n = splice(fds[0], nullptr, udp_sock.get(), nullptr, sizeof(buf), 0); - if (IsRunningOnGvisor()) { - EXPECT_THAT(n, SyscallFailsWithErrno(EMSGSIZE)); - } else { - // TODO(gvisor.dev/issue/138): Linux sends out multiple UDP datagrams, each - // of the size of a page. - EXPECT_THAT(n, SyscallSucceedsWithValue(sizeof(buf))); - } -} - -INSTANTIATE_TEST_SUITE_P(AllRawPacketMsgSizeTest, RawPacketMsgSizeTest, - ::testing::Values(V4Loopback(), V6Loopback())); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc deleted file mode 100644 index 13afa0eaf..000000000 --- a/test/syscalls/linux/partial_bad_buffer.cc +++ /dev/null @@ -1,408 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::Gt; - -namespace gvisor { -namespace testing { - -namespace { - -constexpr char kMessage[] = "hello world"; - -// PartialBadBufferTest checks the result of various IO syscalls when passed a -// buffer that does not have the space specified in the syscall (most of it is -// PROT_NONE). Linux is annoyingly inconsistent among different syscalls, so we -// test all of them. -class PartialBadBufferTest : public ::testing::Test { - protected: - void SetUp() override { - // Create and open a directory for getdents cases. - directory_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT( - directory_fd_ = open(directory_.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - - // Create and open a normal file, placing it in the directory - // so the getdents cases have some dirents. - name_ = JoinPath(directory_.path(), "a"); - ASSERT_THAT(fd_ = open(name_.c_str(), O_RDWR | O_CREAT, 0644), - SyscallSucceeds()); - - // Write some initial data. - size_t size = sizeof(kMessage) - 1; - EXPECT_THAT(WriteFd(fd_, &kMessage, size), SyscallSucceedsWithValue(size)); - ASSERT_THAT(lseek(fd_, 0, SEEK_SET), SyscallSucceeds()); - - // Map a useable buffer. - addr_ = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(addr_, MAP_FAILED); - char* buf = reinterpret_cast<char*>(addr_); - - // Guard page for our read to run into. - ASSERT_THAT(mprotect(reinterpret_cast<void*>(buf + kPageSize), kPageSize, - PROT_NONE), - SyscallSucceeds()); - - // Leave only one free byte in the buffer. - bad_buffer_ = buf + kPageSize - 1; - } - - off_t Size() { - struct stat st; - int rc = fstat(fd_, &st); - if (rc < 0) { - return static_cast<off_t>(rc); - } - return st.st_size; - } - - void TearDown() override { - EXPECT_THAT(munmap(addr_, 2 * kPageSize), SyscallSucceeds()) << addr_; - EXPECT_THAT(close(fd_), SyscallSucceeds()); - EXPECT_THAT(unlink(name_.c_str()), SyscallSucceeds()); - EXPECT_THAT(close(directory_fd_), SyscallSucceeds()); - } - - // Return buffer with n bytes of free space. - // N.B. this is the same buffer used to back bad_buffer_. - char* FreeBytes(size_t n) { - TEST_CHECK(n <= static_cast<size_t>(4096)); - return reinterpret_cast<char*>(addr_) + kPageSize - n; - } - - std::string name_; - int fd_; - TempPath directory_; - int directory_fd_; - void* addr_; - char* bad_buffer_; -}; - -// We do both "big" and "small" tests to try to hit the "zero copy" and -// non-"zero copy" paths, which have different code paths for handling faults. - -TEST_F(PartialBadBufferTest, ReadBig) { - EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, kPageSize), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, ReadSmall) { - EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, 10), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadBig) { - EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, kPageSize, 0), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadSmall) { - EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, 10, 0), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, ReadvBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - - EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, ReadvSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - - EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadvBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - - EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadvSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - - EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, WriteBig) { - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(write)(fd_, bad_buffer_, kPageSize)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, WriteSmall) { - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(write)(fd_, bad_buffer_, 10)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwriteBig) { - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwrite)(fd_, bad_buffer_, kPageSize, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwriteSmall) { - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwrite)(fd_, bad_buffer_, 10, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, WritevBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(writev)(fd_, &vec, 1)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, WritevSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(writev)(fd_, &vec, 1)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwritevBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwritev)(fd_, &vec, 1, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwritevSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwritev)(fd_, &vec, 1, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -// getdents returns EFAULT when the you claim the buffer is large enough, but -// it actually isn't. -TEST_F(PartialBadBufferTest, GetdentsBig) { - EXPECT_THAT(RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, - kPageSize), - SyscallFailsWithErrno(EFAULT)); -} - -// getdents returns EINVAL when the you claim the buffer is too small. -TEST_F(PartialBadBufferTest, GetdentsSmall) { - EXPECT_THAT( - RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, 10), - SyscallFailsWithErrno(EINVAL)); -} - -// getdents will write entries into a buffer if there is space before it faults. -TEST_F(PartialBadBufferTest, GetdentsOneEntry) { - // 30 bytes is enough for one (small) entry. - char* buf = FreeBytes(30); - - EXPECT_THAT( - RetryEINTR(syscall)(SYS_getdents64, directory_fd_, buf, kPageSize), - SyscallSucceedsWithValue(Gt(0))); -} - -PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - addr.ss_family = family; - switch (family) { - case AF_INET: - reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr = - htonl(INADDR_LOOPBACK); - break; - case AF_INET6: - reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr = - in6addr_loopback; - break; - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } - return addr; -} - -// SendMsgTCP verifies that calling sendmsg with a bad address returns an -// EFAULT. It also verifies that passing a buffer which is made up of 2 -// pages one valid and one guard page succeeds as long as the write is -// for exactly the size of 1 page. -TEST_F(PartialBadBufferTest, SendMsgTCP_NoRandomSave) { - // FIXME(b/171436815): Netstack save/restore is broken. - const DisableSave ds; - - auto listen_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET)); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT(bind(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds()); - - // Get the address we're listening on, then connect to it. We need to do this - // because we're allowing the stack to pick a port for us. - ASSERT_THAT(getsockname(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - auto send_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT( - RetryEINTR(connect)(send_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Accept the connection. - auto recv_socket = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr)); - - // TODO(gvisor.dev/issue/674): Update this once Netstack matches linux - // behaviour on a setsockopt of SO_RCVBUF/SO_SNDBUF. - // - // Set SO_SNDBUF for socket to exactly kPageSize+1. - // - // gVisor does not double the value passed in SO_SNDBUF like linux does so we - // just increase it by 1 byte here for gVisor so that we can test writing 1 - // byte past the valid page and check that it triggers an EFAULT - // correctly. Otherwise in gVisor the sendmsg call will just return with no - // error with kPageSize bytes written successfully. - const uint32_t buf_size = kPageSize + 1; - ASSERT_THAT(setsockopt(send_socket.get(), SOL_SOCKET, SO_SNDBUF, &buf_size, - sizeof(buf_size)), - SyscallSucceedsWithValue(0)); - - struct msghdr hdr = {}; - struct iovec iov = {}; - iov.iov_base = bad_buffer_; - iov.iov_len = kPageSize; - hdr.msg_iov = &iov; - hdr.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0), - SyscallFailsWithErrno(EFAULT)); - - // Now assert that writing kPageSize from addr_ succeeds. - iov.iov_base = addr_; - ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0), - SyscallSucceedsWithValue(kPageSize)); - // Read all the data out so that we drain the socket SND_BUF on the sender. - std::vector<char> buffer(kPageSize); - ASSERT_THAT(RetryEINTR(read)(recv_socket.get(), buffer.data(), kPageSize), - SyscallSucceedsWithValue(kPageSize)); - - // Sleep for a shortwhile to ensure that we have time to process the - // ACKs. This is not strictly required unless running under gotsan which is a - // lot slower and can result in the next write to write only 1 byte instead of - // our intended kPageSize + 1. - absl::SleepFor(absl::Milliseconds(50)); - - // Now assert that writing > kPageSize results in EFAULT. - iov.iov_len = kPageSize + 1; - ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0), - SyscallFailsWithErrno(EFAULT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/pause.cc b/test/syscalls/linux/pause.cc deleted file mode 100644 index 8c05efd6f..000000000 --- a/test/syscalls/linux/pause.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <atomic> - -#include "gtest/gtest.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void NoopSignalHandler(int sig, siginfo_t* info, void* context) {} - -} // namespace - -TEST(PauseTest, OnlyReturnsWhenSignalHandled) { - struct sigaction sa; - sigfillset(&sa.sa_mask); - - // Ensure that SIGUSR1 is ignored. - sa.sa_handler = SIG_IGN; - ASSERT_THAT(sigaction(SIGUSR1, &sa, nullptr), SyscallSucceeds()); - - // Register a handler for SIGUSR2. - sa.sa_sigaction = NoopSignalHandler; - sa.sa_flags = SA_SIGINFO; - ASSERT_THAT(sigaction(SIGUSR2, &sa, nullptr), SyscallSucceeds()); - - // The child sets their own tid. - absl::Mutex mu; - pid_t child_tid = 0; - bool child_tid_available = false; - std::atomic<int> sent_signal{0}; - std::atomic<int> waking_signal{0}; - ScopedThread t([&] { - mu.Lock(); - child_tid = gettid(); - child_tid_available = true; - mu.Unlock(); - EXPECT_THAT(pause(), SyscallFailsWithErrno(EINTR)); - waking_signal.store(sent_signal.load()); - }); - mu.Lock(); - mu.Await(absl::Condition(&child_tid_available)); - mu.Unlock(); - - // Wait a bit to let the child enter pause(). - absl::SleepFor(absl::Seconds(3)); - - // The child should not be woken by SIGUSR1. - sent_signal.store(SIGUSR1); - ASSERT_THAT(tgkill(getpid(), child_tid, SIGUSR1), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(3)); - - // The child should be woken by SIGUSR2. - sent_signal.store(SIGUSR2); - ASSERT_THAT(tgkill(getpid(), child_tid, SIGUSR2), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(3)); - - EXPECT_EQ(SIGUSR2, waking_signal.load()); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ping_socket.cc b/test/syscalls/linux/ping_socket.cc deleted file mode 100644 index 999c8ab6b..000000000 --- a/test/syscalls/linux/ping_socket.cc +++ /dev/null @@ -1,76 +0,0 @@ -// 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 <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -// Test ICMP port exhaustion returns EAGAIN. -// -// We disable both random/cooperative S/R for this test as it makes way too many -// syscalls. -TEST(PingSocket, ICMPPortExhaustion_NoRandomSave) { - DisableSave ds; - - { - auto s = Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); - if (!s.ok()) { - ASSERT_EQ(s.error().errno_value(), EACCES); - GTEST_SKIP(); - } - } - - const struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_addr = - { - .s_addr = htonl(INADDR_LOOPBACK), - }, - }; - - std::vector<FileDescriptor> sockets; - constexpr int kSockets = 65536; - for (int i = 0; i < kSockets; i++) { - auto s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); - int ret = connect(s.get(), reinterpret_cast<const struct sockaddr*>(&addr), - sizeof(addr)); - if (ret == 0) { - sockets.push_back(std::move(s)); - continue; - } - ASSERT_THAT(ret, SyscallFailsWithErrno(EAGAIN)); - break; - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc deleted file mode 100644 index 01ccbdcd2..000000000 --- a/test/syscalls/linux/pipe.cc +++ /dev/null @@ -1,690 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> /* Obtain O_* constant definitions */ -#include <linux/magic.h> -#include <sys/ioctl.h> -#include <sys/statfs.h> -#include <sys/uio.h> -#include <unistd.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/synchronization/notification.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Used as a non-zero sentinel value, below. -constexpr int kTestValue = 0x12345678; - -// Used for synchronization in race tests. -const absl::Duration syncDelay = absl::Seconds(2); - -struct PipeCreator { - std::string name_; - - // void (fds, is_blocking, is_namedpipe). - std::function<void(int[2], bool*, bool*)> create_; -}; - -class PipeTest : public ::testing::TestWithParam<PipeCreator> { - public: - static void SetUpTestSuite() { - // Tests intentionally generate SIGPIPE. - TEST_PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR); - } - - // Initializes rfd_ and wfd_ as a blocking pipe. - // - // The return value indicates success: the test should be skipped otherwise. - bool CreateBlocking() { return create(true); } - - // Initializes rfd_ and wfd_ as a non-blocking pipe. - // - // The return value is per CreateBlocking. - bool CreateNonBlocking() { return create(false); } - - // Returns true iff the pipe represents a named pipe. - bool IsNamedPipe() const { return named_pipe_; } - - size_t Size() const { - int s1 = fcntl(rfd_.get(), F_GETPIPE_SZ); - int s2 = fcntl(wfd_.get(), F_GETPIPE_SZ); - EXPECT_GT(s1, 0); - EXPECT_GT(s2, 0); - EXPECT_EQ(s1, s2); - return static_cast<size_t>(s1); - } - - static void TearDownTestSuite() { - TEST_PCHECK(signal(SIGPIPE, SIG_DFL) != SIG_ERR); - } - - private: - bool create(bool wants_blocking) { - // Generate the pipe. - int fds[2] = {-1, -1}; - bool is_blocking = false; - GetParam().create_(fds, &is_blocking, &named_pipe_); - if (fds[0] < 0 || fds[1] < 0) { - return false; - } - - // Save descriptors. - rfd_.reset(fds[0]); - wfd_.reset(fds[1]); - - // Adjust blocking, if needed. - if (!is_blocking && wants_blocking) { - // Clear the blocking flag. - EXPECT_THAT(fcntl(fds[0], F_SETFL, 0), SyscallSucceeds()); - EXPECT_THAT(fcntl(fds[1], F_SETFL, 0), SyscallSucceeds()); - } else if (is_blocking && !wants_blocking) { - // Set the descriptors to blocking. - EXPECT_THAT(fcntl(fds[0], F_SETFL, O_NONBLOCK), SyscallSucceeds()); - EXPECT_THAT(fcntl(fds[1], F_SETFL, O_NONBLOCK), SyscallSucceeds()); - } - - return true; - } - - protected: - FileDescriptor rfd_; - FileDescriptor wfd_; - - private: - bool named_pipe_ = false; -}; - -TEST_P(PipeTest, Inode) { - SKIP_IF(!CreateBlocking()); - - // Ensure that the inode number is the same for each end. - struct stat rst; - ASSERT_THAT(fstat(rfd_.get(), &rst), SyscallSucceeds()); - struct stat wst; - ASSERT_THAT(fstat(wfd_.get(), &wst), SyscallSucceeds()); - EXPECT_EQ(rst.st_ino, wst.st_ino); -} - -TEST_P(PipeTest, Permissions) { - SKIP_IF(!CreateBlocking()); - - // Attempt bad operations. - int buf = kTestValue; - ASSERT_THAT(write(rfd_.get(), &buf, sizeof(buf)), - SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(read(wfd_.get(), &buf, sizeof(buf)), - SyscallFailsWithErrno(EBADF)); -} - -TEST_P(PipeTest, Flags) { - SKIP_IF(!CreateBlocking()); - - if (IsNamedPipe()) { - // May be stubbed to zero; define locally. - EXPECT_THAT(fcntl(rfd_.get(), F_GETFL), - SyscallSucceedsWithValue(kOLargeFile | O_RDONLY)); - EXPECT_THAT(fcntl(wfd_.get(), F_GETFL), - SyscallSucceedsWithValue(kOLargeFile | O_WRONLY)); - } else { - EXPECT_THAT(fcntl(rfd_.get(), F_GETFL), SyscallSucceedsWithValue(O_RDONLY)); - EXPECT_THAT(fcntl(wfd_.get(), F_GETFL), SyscallSucceedsWithValue(O_WRONLY)); - } -} - -TEST_P(PipeTest, Write) { - SKIP_IF(!CreateBlocking()); - - int wbuf = kTestValue; - int rbuf = ~kTestValue; - ASSERT_THAT(write(wfd_.get(), &wbuf, sizeof(wbuf)), - SyscallSucceedsWithValue(sizeof(wbuf))); - ASSERT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(wbuf, rbuf); -} - -TEST_P(PipeTest, WritePage) { - SKIP_IF(!CreateBlocking()); - - std::vector<char> wbuf(kPageSize); - RandomizeBuffer(wbuf.data(), wbuf.size()); - std::vector<char> rbuf(wbuf.size()); - - ASSERT_THAT(write(wfd_.get(), wbuf.data(), wbuf.size()), - SyscallSucceedsWithValue(wbuf.size())); - ASSERT_THAT(read(rfd_.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(rbuf.size())); - EXPECT_EQ(memcmp(rbuf.data(), wbuf.data(), wbuf.size()), 0); -} - -TEST_P(PipeTest, NonBlocking) { - SKIP_IF(!CreateNonBlocking()); - - int wbuf = kTestValue; - int rbuf = ~kTestValue; - EXPECT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)), - SyscallFailsWithErrno(EWOULDBLOCK)); - ASSERT_THAT(write(wfd_.get(), &wbuf, sizeof(wbuf)), - SyscallSucceedsWithValue(sizeof(wbuf))); - - ASSERT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(wbuf, rbuf); - EXPECT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST(PipeTest, StatFS) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - struct statfs st; - EXPECT_THAT(fstatfs(fds[0], &st), SyscallSucceeds()); - EXPECT_EQ(st.f_type, PIPEFS_MAGIC); - EXPECT_EQ(st.f_bsize, getpagesize()); - EXPECT_EQ(st.f_namelen, NAME_MAX); -} - -TEST(Pipe2Test, CloExec) { - int fds[2]; - ASSERT_THAT(pipe2(fds, O_CLOEXEC), SyscallSucceeds()); - EXPECT_THAT(fcntl(fds[0], F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - EXPECT_THAT(fcntl(fds[1], F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); - EXPECT_THAT(close(fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(fds[1]), SyscallSucceeds()); -} - -TEST(Pipe2Test, BadOptions) { - int fds[2]; - EXPECT_THAT(pipe2(fds, 0xDEAD), SyscallFailsWithErrno(EINVAL)); -} - -// Tests that opening named pipes with O_TRUNC shouldn't cause an error, but -// calls to (f)truncate should. -TEST(NamedPipeTest, Truncate) { - const std::string tmp_path = NewTempAbsPath(); - SKIP_IF(mkfifo(tmp_path.c_str(), 0644) != 0); - - ASSERT_THAT(open(tmp_path.c_str(), O_NONBLOCK | O_RDONLY), SyscallSucceeds()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(tmp_path.c_str(), O_RDWR | O_NONBLOCK | O_TRUNC)); - - ASSERT_THAT(truncate(tmp_path.c_str(), 0), SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(PipeTest, Seek) { - SKIP_IF(!CreateBlocking()); - - for (int i = 0; i < 4; i++) { - // Attempt absolute seeks. - EXPECT_THAT(lseek(rfd_.get(), 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(rfd_.get(), 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(wfd_.get(), 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(wfd_.get(), 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); - - // Attempt relative seeks. - EXPECT_THAT(lseek(rfd_.get(), 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(rfd_.get(), 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(wfd_.get(), 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(wfd_.get(), 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); - - // Attempt end-of-file seeks. - EXPECT_THAT(lseek(rfd_.get(), 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(rfd_.get(), -4, SEEK_END), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(wfd_.get(), 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(lseek(wfd_.get(), -4, SEEK_END), SyscallFailsWithErrno(ESPIPE)); - - // Add some more data to the pipe. - int buf = kTestValue; - ASSERT_THAT(write(wfd_.get(), &buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - } -} - -TEST_P(PipeTest, OffsetCalls) { - SKIP_IF(!CreateBlocking()); - - int buf; - EXPECT_THAT(pread(wfd_.get(), &buf, sizeof(buf), 0), - SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(pwrite(rfd_.get(), &buf, sizeof(buf), 0), - SyscallFailsWithErrno(ESPIPE)); - - struct iovec iov; - iov.iov_base = &buf; - iov.iov_len = sizeof(buf); - EXPECT_THAT(preadv(wfd_.get(), &iov, 1, 0), SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(pwritev(rfd_.get(), &iov, 1, 0), SyscallFailsWithErrno(ESPIPE)); -} - -TEST_P(PipeTest, WriterSideCloses) { - SKIP_IF(!CreateBlocking()); - - ScopedThread t([this]() { - int buf = ~kTestValue; - ASSERT_THAT(read(rfd_.get(), &buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - EXPECT_EQ(buf, kTestValue); - // This will return when the close() completes. - ASSERT_THAT(read(rfd_.get(), &buf, sizeof(buf)), SyscallSucceeds()); - // This will return straight away. - ASSERT_THAT(read(rfd_.get(), &buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); - }); - - // Sleep a bit so the thread can block. - absl::SleepFor(syncDelay); - - // Write to unblock. - int buf = kTestValue; - ASSERT_THAT(write(wfd_.get(), &buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Sleep a bit so the thread can block again. - absl::SleepFor(syncDelay); - - // Allow the thread to complete. - ASSERT_THAT(close(wfd_.release()), SyscallSucceeds()); - t.Join(); -} - -TEST_P(PipeTest, WriterSideClosesReadDataFirst) { - SKIP_IF(!CreateBlocking()); - - int wbuf = kTestValue; - ASSERT_THAT(write(wfd_.get(), &wbuf, sizeof(wbuf)), - SyscallSucceedsWithValue(sizeof(wbuf))); - ASSERT_THAT(close(wfd_.release()), SyscallSucceeds()); - - int rbuf; - ASSERT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(wbuf, rbuf); - EXPECT_THAT(read(rfd_.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(0)); -} - -TEST_P(PipeTest, ReaderSideCloses) { - SKIP_IF(!CreateBlocking()); - - ASSERT_THAT(close(rfd_.release()), SyscallSucceeds()); - int buf = kTestValue; - EXPECT_THAT(write(wfd_.get(), &buf, sizeof(buf)), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(PipeTest, CloseTwice) { - SKIP_IF(!CreateBlocking()); - - int reader = rfd_.release(); - int writer = wfd_.release(); - ASSERT_THAT(close(reader), SyscallSucceeds()); - ASSERT_THAT(close(writer), SyscallSucceeds()); - EXPECT_THAT(close(reader), SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(close(writer), SyscallFailsWithErrno(EBADF)); -} - -// Blocking write returns EPIPE when read end is closed if nothing has been -// written. -TEST_P(PipeTest, BlockWriteClosed) { - SKIP_IF(!CreateBlocking()); - - absl::Notification notify; - ScopedThread t([this, ¬ify]() { - std::vector<char> buf(Size()); - // Exactly fill the pipe buffer. - ASSERT_THAT(WriteFd(wfd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - notify.Notify(); - - // Attempt to write one more byte. Blocks. - // N.B. Don't use WriteFd, we don't want a retry. - EXPECT_THAT(write(wfd_.get(), buf.data(), 1), SyscallFailsWithErrno(EPIPE)); - }); - - notify.WaitForNotification(); - ASSERT_THAT(close(rfd_.release()), SyscallSucceeds()); - t.Join(); -} - -// Blocking write returns EPIPE when read end is closed even if something has -// been written. -TEST_P(PipeTest, BlockPartialWriteClosed) { - SKIP_IF(!CreateBlocking()); - - ScopedThread t([this]() { - const int pipe_size = Size(); - std::vector<char> buf(2 * pipe_size); - - // Write more than fits in the buffer. Blocks then returns partial write - // when the other end is closed. The next call returns EPIPE. - ASSERT_THAT(write(wfd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(pipe_size)); - EXPECT_THAT(write(wfd_.get(), buf.data(), buf.size()), - SyscallFailsWithErrno(EPIPE)); - }); - - // Leave time for write to become blocked. - absl::SleepFor(syncDelay); - - // Unblock the above. - ASSERT_THAT(close(rfd_.release()), SyscallSucceeds()); - t.Join(); -} - -TEST_P(PipeTest, ReadFromClosedFd_NoRandomSave) { - SKIP_IF(!CreateBlocking()); - - absl::Notification notify; - ScopedThread t([this, ¬ify]() { - notify.Notify(); - int buf; - ASSERT_THAT(read(rfd_.get(), &buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_EQ(kTestValue, buf); - }); - notify.WaitForNotification(); - - // Make sure that the thread gets to read(). - absl::SleepFor(syncDelay); - - { - // We cannot save/restore here as the read end of pipe is closed but there - // is ongoing read() above. We will not be able to restart the read() - // successfully in restore run since the read fd is closed. - const DisableSave ds; - ASSERT_THAT(close(rfd_.release()), SyscallSucceeds()); - int buf = kTestValue; - ASSERT_THAT(write(wfd_.get(), &buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - t.Join(); - } -} - -TEST_P(PipeTest, FionRead) { - SKIP_IF(!CreateBlocking()); - - int n; - ASSERT_THAT(ioctl(rfd_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - ASSERT_THAT(ioctl(wfd_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - std::vector<char> buf(Size()); - ASSERT_THAT(write(wfd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - EXPECT_THAT(ioctl(rfd_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, buf.size()); - EXPECT_THAT(ioctl(wfd_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, buf.size()); -} - -// Test that opening an empty anonymous pipe RDONLY via /proc/self/fd/N does not -// block waiting for a writer. -TEST_P(PipeTest, OpenViaProcSelfFD) { - SKIP_IF(!CreateBlocking()); - SKIP_IF(IsNamedPipe()); - - // Close the write end of the pipe. - ASSERT_THAT(close(wfd_.release()), SyscallSucceeds()); - - // Open other side via /proc/self/fd. It should not block. - FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(absl::StrCat("/proc/self/fd/", rfd_.get()), O_RDONLY)); -} - -// Test that opening and reading from an anonymous pipe (with existing writes) -// RDONLY via /proc/self/fd/N returns the existing data. -TEST_P(PipeTest, OpenViaProcSelfFDWithWrites) { - SKIP_IF(!CreateBlocking()); - SKIP_IF(IsNamedPipe()); - - // Write to the pipe and then close the write fd. - int wbuf = kTestValue; - ASSERT_THAT(write(wfd_.get(), &wbuf, sizeof(wbuf)), - SyscallSucceedsWithValue(sizeof(wbuf))); - ASSERT_THAT(close(wfd_.release()), SyscallSucceeds()); - - // Open read side via /proc/self/fd, and read from it. - FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(absl::StrCat("/proc/self/fd/", rfd_.get()), O_RDONLY)); - int rbuf; - ASSERT_THAT(read(proc_self_fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(wbuf, rbuf); -} - -// Test that accesses of /proc/<PID>/fd correctly decrement the refcount. -TEST_P(PipeTest, ProcFDReleasesFile) { - SKIP_IF(!CreateBlocking()); - - // Stat the pipe FD, which shouldn't alter the refcount. - struct stat wst; - ASSERT_THAT(lstat(absl::StrCat("/proc/self/fd/", wfd_.get()).c_str(), &wst), - SyscallSucceeds()); - - // Close the write end and ensure that read indicates EOF. - wfd_.reset(); - char buf; - ASSERT_THAT(read(rfd_.get(), &buf, 1), SyscallSucceedsWithValue(0)); -} - -// Same for /proc/<PID>/fdinfo. -TEST_P(PipeTest, ProcFDInfoReleasesFile) { - SKIP_IF(!CreateBlocking()); - - // Stat the pipe FD, which shouldn't alter the refcount. - struct stat wst; - ASSERT_THAT( - lstat(absl::StrCat("/proc/self/fdinfo/", wfd_.get()).c_str(), &wst), - SyscallSucceeds()); - - // Close the write end and ensure that read indicates EOF. - wfd_.reset(); - char buf; - ASSERT_THAT(read(rfd_.get(), &buf, 1), SyscallSucceedsWithValue(0)); -} - -TEST_P(PipeTest, SizeChange) { - SKIP_IF(!CreateBlocking()); - - // Set the minimum possible size. - ASSERT_THAT(fcntl(rfd_.get(), F_SETPIPE_SZ, 0), SyscallSucceeds()); - int min = Size(); - EXPECT_GT(min, 0); // Should be rounded up. - - // Set from the read end. - ASSERT_THAT(fcntl(rfd_.get(), F_SETPIPE_SZ, min + 1), SyscallSucceeds()); - int med = Size(); - EXPECT_GT(med, min); // Should have grown, may be rounded. - - // Set from the write end. - ASSERT_THAT(fcntl(wfd_.get(), F_SETPIPE_SZ, med + 1), SyscallSucceeds()); - int max = Size(); - EXPECT_GT(max, med); // Ditto. -} - -TEST_P(PipeTest, SizeChangeMax) { - SKIP_IF(!CreateBlocking()); - - // Assert there's some maximum. - EXPECT_THAT(fcntl(rfd_.get(), F_SETPIPE_SZ, 0x7fffffffffffffff), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(fcntl(wfd_.get(), F_SETPIPE_SZ, 0x7fffffffffffffff), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(PipeTest, SizeChangeFull) { - SKIP_IF(!CreateBlocking()); - - // Ensure that we adjust to a large enough size to avoid rounding when we - // perform the size decrease. If rounding occurs, we may not actually - // adjust the size and the call below will return success. It was found via - // experimentation that this granularity avoids the rounding for Linux. - constexpr int kDelta = 64 * 1024; - ASSERT_THAT(fcntl(wfd_.get(), F_SETPIPE_SZ, Size() + kDelta), - SyscallSucceeds()); - - // Fill the buffer and try to change down. - std::vector<char> buf(Size()); - ASSERT_THAT(write(wfd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - EXPECT_THAT(fcntl(wfd_.get(), F_SETPIPE_SZ, Size() - kDelta), - SyscallFailsWithErrno(EBUSY)); -} - -TEST_P(PipeTest, Streaming) { - SKIP_IF(!CreateBlocking()); - - // We make too many calls to go through full save cycles. - DisableSave ds; - - // Size() requires 2 syscalls, call it once and remember the value. - const size_t pipe_size = Size(); - const size_t streamed_bytes = 4 * pipe_size; - - absl::Notification notify; - ScopedThread t([&, this]() { - std::vector<char> buf(1024); - // Don't start until it's full. - notify.WaitForNotification(); - size_t total = 0; - while (total < streamed_bytes) { - ASSERT_THAT(read(rfd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - total += buf.size(); - } - }); - - // Write 4 bytes * pipe_size. It will fill up the pipe once, notify the reader - // to start. Then we write pipe size worth 3 more times to ensure the reader - // can follow along. - // - // The size of each write (which is determined by buf.size()) must be smaller - // than the size of the pipe (which, in the "smallbuffer" configuration, is 1 - // page) for the check for notify.Notify() below to be correct. - std::vector<char> buf(1024); - RandomizeBuffer(buf.data(), buf.size()); - size_t total = 0; - while (total < streamed_bytes) { - ASSERT_THAT(write(wfd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - total += buf.size(); - - // Is the next write about to fill up the buffer? Wake up the reader once. - if (total < pipe_size && (total + buf.size()) >= pipe_size) { - notify.Notify(); - } - } -} - -std::string PipeCreatorName(::testing::TestParamInfo<PipeCreator> info) { - return info.param.name_; // Use the name specified. -} - -INSTANTIATE_TEST_SUITE_P( - Pipes, PipeTest, - ::testing::Values( - PipeCreator{ - "pipe", - [](int fds[2], bool* is_blocking, bool* is_namedpipe) { - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - *is_blocking = true; - *is_namedpipe = false; - }, - }, - PipeCreator{ - "pipe2blocking", - [](int fds[2], bool* is_blocking, bool* is_namedpipe) { - ASSERT_THAT(pipe2(fds, 0), SyscallSucceeds()); - *is_blocking = true; - *is_namedpipe = false; - }, - }, - PipeCreator{ - "pipe2nonblocking", - [](int fds[2], bool* is_blocking, bool* is_namedpipe) { - ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds()); - *is_blocking = false; - *is_namedpipe = false; - }, - }, - PipeCreator{ - "smallbuffer", - [](int fds[2], bool* is_blocking, bool* is_namedpipe) { - // Set to the minimum available size (will round up). - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - ASSERT_THAT(fcntl(fds[0], F_SETPIPE_SZ, 0), SyscallSucceeds()); - *is_blocking = true; - *is_namedpipe = false; - }, - }, - PipeCreator{ - "namednonblocking", - [](int fds[2], bool* is_blocking, bool* is_namedpipe) { - // Create a new file-based pipe (non-blocking). - std::string path; - { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - path = file.path(); - } - SKIP_IF(mkfifo(path.c_str(), 0644) != 0); - fds[0] = open(path.c_str(), O_NONBLOCK | O_RDONLY); - fds[1] = open(path.c_str(), O_NONBLOCK | O_WRONLY); - MaybeSave(); - *is_blocking = false; - *is_namedpipe = true; - }, - }, - PipeCreator{ - "namedblocking", - [](int fds[2], bool* is_blocking, bool* is_namedpipe) { - // Create a new file-based pipe (blocking). - std::string path; - { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - path = file.path(); - } - SKIP_IF(mkfifo(path.c_str(), 0644) != 0); - ScopedThread t( - [&path, &fds]() { fds[1] = open(path.c_str(), O_WRONLY); }); - fds[0] = open(path.c_str(), O_RDONLY); - t.Join(); - MaybeSave(); - *is_blocking = true; - *is_namedpipe = true; - }, - }), - PipeCreatorName); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc deleted file mode 100644 index 7a316427d..000000000 --- a/test/syscalls/linux/poll.cc +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <poll.h> -#include <sys/resource.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include <algorithm> -#include <iostream> - -#include "gtest/gtest.h" -#include "absl/synchronization/notification.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/base_poll_test.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { -namespace { - -class PollTest : public BasePollTest { - protected: - void SetUp() override { BasePollTest::SetUp(); } - void TearDown() override { BasePollTest::TearDown(); } -}; - -TEST_F(PollTest, InvalidFds) { - // fds is invalid because it's null, but we tell ppoll the length is non-zero. - EXPECT_THAT(poll(nullptr, 1, 1), SyscallFailsWithErrno(EFAULT)); - EXPECT_THAT(poll(nullptr, -1, 1), SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(PollTest, NullFds) { - EXPECT_THAT(poll(nullptr, 0, 10), SyscallSucceeds()); -} - -TEST_F(PollTest, ZeroTimeout) { - EXPECT_THAT(poll(nullptr, 0, 0), SyscallSucceeds()); -} - -// If random S/R interrupts the poll, SIGALRM may be delivered before poll -// restarts, causing the poll to hang forever. -TEST_F(PollTest, NegativeTimeout_NoRandomSave) { - // Negative timeout mean wait forever so set a timer. - SetTimer(absl::Milliseconds(100)); - EXPECT_THAT(poll(nullptr, 0, -1), SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); -} - -TEST_F(PollTest, NonBlockingEventPOLLIN) { - // Create a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Write some data to the pipe. - char s[] = "foo\n"; - ASSERT_THAT(WriteFd(fd1.get(), s, strlen(s) + 1), SyscallSucceeds()); - - // Poll on the reader fd with POLLIN event. - struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1)); - - // Should trigger POLLIN event. - EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); -} - -TEST_F(PollTest, BlockingEventPOLLIN) { - // Create a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Start a blocking poll on the read fd. - absl::Notification notify; - ScopedThread t([&fd0, ¬ify]() { - notify.Notify(); - - // Poll on the reader fd with POLLIN event. - struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, -1), SyscallSucceedsWithValue(1)); - - // Should trigger POLLIN event. - EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); - }); - - notify.WaitForNotification(); - absl::SleepFor(absl::Seconds(1.0)); - - // Write some data to the pipe. - char s[] = "foo\n"; - ASSERT_THAT(WriteFd(fd1.get(), s, strlen(s) + 1), SyscallSucceeds()); -} - -TEST_F(PollTest, NonBlockingEventPOLLHUP) { - // Create a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Close the writer fd. - fd1.reset(); - - // Poll on the reader fd with POLLIN event. - struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1)); - - // Should trigger POLLHUP event. - EXPECT_EQ(poll_fd.revents & POLLHUP, POLLHUP); - - // Should not trigger POLLIN event. - EXPECT_EQ(poll_fd.revents & POLLIN, 0); -} - -TEST_F(PollTest, BlockingEventPOLLHUP) { - // Create a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Start a blocking poll on the read fd. - absl::Notification notify; - ScopedThread t([&fd0, ¬ify]() { - notify.Notify(); - - // Poll on the reader fd with POLLIN event. - struct pollfd poll_fd = {fd0.get(), POLLIN, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, -1), SyscallSucceedsWithValue(1)); - - // Should trigger POLLHUP event. - EXPECT_EQ(poll_fd.revents & POLLHUP, POLLHUP); - - // Should not trigger POLLIN event. - EXPECT_EQ(poll_fd.revents & POLLIN, 0); - }); - - notify.WaitForNotification(); - absl::SleepFor(absl::Seconds(1.0)); - - // Write some data and close the writer fd. - fd1.reset(); -} - -TEST_F(PollTest, NonBlockingEventPOLLERR) { - // Create a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Close the reader fd. - fd0.reset(); - - // Poll on the writer fd with POLLOUT event. - struct pollfd poll_fd = {fd1.get(), POLLOUT, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 0), SyscallSucceedsWithValue(1)); - - // Should trigger POLLERR event. - EXPECT_EQ(poll_fd.revents & POLLERR, POLLERR); - - // Should also trigger POLLOUT event. - EXPECT_EQ(poll_fd.revents & POLLOUT, POLLOUT); -} - -// This test will validate that if an FD is already ready on some event, whether -// it's POLLIN or POLLOUT it will not immediately return unless that's actually -// what the caller was interested in. -TEST_F(PollTest, ImmediatelyReturnOnlyOnPollEvents) { - // Create a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Wait for read related event on the write side of the pipe, since a write - // is possible on fds[1] it would mean that POLLOUT would return immediately. - // We should make sure that we're not woken up with that state that we didn't - // specificially request. - constexpr int kTimeoutMs = 100; - struct pollfd poll_fd = {fd1.get(), POLLIN | POLLPRI | POLLRDHUP, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kTimeoutMs), - SyscallSucceedsWithValue(0)); // We should timeout. - EXPECT_EQ(poll_fd.revents, 0); // Nothing should be in returned events. - - // Now let's poll on POLLOUT and we should get back 1 fd as being ready and - // it should contain POLLOUT in the revents. - poll_fd.events = POLLOUT; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kTimeoutMs), - SyscallSucceedsWithValue(1)); // 1 fd should have an event. - EXPECT_EQ(poll_fd.revents, POLLOUT); // POLLOUT should be in revents. -} - -// This test validates that poll(2) while data is available immediately returns. -TEST_F(PollTest, PollLevelTriggered) { - int fds[2] = {}; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, /*protocol=*/0, fds), - SyscallSucceeds()); - - FileDescriptor fd0(fds[0]); - FileDescriptor fd1(fds[1]); - - // Write two bytes to the socket. - const char* kBuf = "aa"; - ASSERT_THAT(RetryEINTR(send)(fd0.get(), kBuf, /*len=*/2, /*flags=*/0), - SyscallSucceedsWithValue(2)); // 2 bytes should be written. - - // Poll(2) should immediately return as there is data available to read. - constexpr int kInfiniteTimeout = -1; - struct pollfd poll_fd = {fd1.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, /*nfds=*/1, kInfiniteTimeout), - SyscallSucceedsWithValue(1)); // 1 fd should be ready to read. - EXPECT_NE(poll_fd.revents & POLLIN, 0); - - // Read a single byte. - char read_byte = 0; - ASSERT_THAT(RetryEINTR(recv)(fd1.get(), &read_byte, /*len=*/1, /*flags=*/0), - SyscallSucceedsWithValue(1)); // 1 byte should be read. - ASSERT_EQ(read_byte, 'a'); // We should have read a single 'a'. - - // Create a separate pollfd for our second poll. - struct pollfd poll_fd_after = {fd1.get(), POLLIN, 0}; - - // Poll(2) should again immediately return since we only read one byte. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd_after, /*nfds=*/1, kInfiniteTimeout), - SyscallSucceedsWithValue(1)); // 1 fd should be ready to read. - EXPECT_NE(poll_fd_after.revents & POLLIN, 0); -} - -TEST_F(PollTest, Nfds) { - // Stash value of RLIMIT_NOFILES. - struct rlimit rlim; - TEST_PCHECK(getrlimit(RLIMIT_NOFILE, &rlim) == 0); - - // gVisor caps the number of FDs that epoll can use beyond RLIMIT_NOFILE. - constexpr rlim_t maxFD = 4096; - if (rlim.rlim_cur > maxFD) { - rlim.rlim_cur = maxFD; - TEST_PCHECK(setrlimit(RLIMIT_NOFILE, &rlim) == 0); - } - - rlim_t max_fds = rlim.rlim_cur; - std::cout << "Using limit: " << max_fds << std::endl; - - // Create an eventfd. Since its value is initially zero, it is writable. - FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - - // Create the biggest possible pollfd array such that each element is valid. - // Each entry in the 'fds' array refers to the eventfd and polls for - // "writable" events (events=POLLOUT). This essentially guarantees that the - // poll() is a no-op and allows negative testing of the 'nfds' parameter. - std::vector<struct pollfd> fds(max_fds + 1, - {.fd = efd.get(), .events = POLLOUT}); - - // Verify that 'nfds' up to RLIMIT_NOFILE are allowed. - EXPECT_THAT(RetryEINTR(poll)(fds.data(), 1, 1), SyscallSucceedsWithValue(1)); - EXPECT_THAT(RetryEINTR(poll)(fds.data(), max_fds / 2, 1), - SyscallSucceedsWithValue(max_fds / 2)); - EXPECT_THAT(RetryEINTR(poll)(fds.data(), max_fds, 1), - SyscallSucceedsWithValue(max_fds)); - - // If 'nfds' exceeds RLIMIT_NOFILE then it must fail with EINVAL. - EXPECT_THAT(poll(fds.data(), max_fds + 1, 1), SyscallFailsWithErrno(EINVAL)); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ppoll.cc b/test/syscalls/linux/ppoll.cc deleted file mode 100644 index 8245a11e8..000000000 --- a/test/syscalls/linux/ppoll.cc +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <poll.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/base_poll_test.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -// Linux and glibc have a different idea of the sizeof sigset_t. When calling -// the syscall directly, use what the kernel expects. -unsigned kSigsetSize = SIGRTMAX / 8; - -// Linux ppoll(2) differs from the glibc wrapper function in that Linux updates -// the timeout with the amount of time remaining. In order to test this behavior -// we need to use the syscall directly. -int syscallPpoll(struct pollfd* fds, nfds_t nfds, struct timespec* timeout_ts, - const sigset_t* sigmask, unsigned mask_size) { - return syscall(SYS_ppoll, fds, nfds, timeout_ts, sigmask, mask_size); -} - -class PpollTest : public BasePollTest { - protected: - void SetUp() override { BasePollTest::SetUp(); } - void TearDown() override { BasePollTest::TearDown(); } -}; - -TEST_F(PpollTest, InvalidFds) { - // fds is invalid because it's null, but we tell ppoll the length is non-zero. - struct timespec timeout = {}; - sigset_t sigmask; - TEST_PCHECK(sigemptyset(&sigmask) == 0); - EXPECT_THAT(syscallPpoll(nullptr, 1, &timeout, &sigmask, kSigsetSize), - SyscallFailsWithErrno(EFAULT)); - EXPECT_THAT(syscallPpoll(nullptr, -1, &timeout, &sigmask, kSigsetSize), - SyscallFailsWithErrno(EINVAL)); -} - -// See that when fds is null, ppoll behaves like sleep. -TEST_F(PpollTest, NullFds) { - struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); - ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_nsec, 0); -} - -TEST_F(PpollTest, ZeroTimeout) { - struct timespec timeout = {}; - ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_nsec, 0); -} - -// If random S/R interrupts the ppoll, SIGALRM may be delivered before ppoll -// restarts, causing the ppoll to hang forever. -TEST_F(PpollTest, NoTimeout_NoRandomSave) { - // When there's no timeout, ppoll may never return so set a timer. - SetTimer(absl::Milliseconds(100)); - // See that we get interrupted by the timer. - ASSERT_THAT(syscallPpoll(nullptr, 0, nullptr, nullptr, 0), - SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); -} - -TEST_F(PpollTest, InvalidTimeoutNegative) { - struct timespec timeout = absl::ToTimespec(absl::Nanoseconds(-1)); - EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(PpollTest, InvalidTimeoutNotNormalized) { - struct timespec timeout = {0, 1000000001}; - EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, nullptr, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(PpollTest, InvalidMaskSize) { - struct timespec timeout = {}; - sigset_t sigmask; - TEST_PCHECK(sigemptyset(&sigmask) == 0); - EXPECT_THAT(syscallPpoll(nullptr, 0, &timeout, &sigmask, 128), - SyscallFailsWithErrno(EINVAL)); -} - -// Verify that signals blocked by the ppoll mask (that would otherwise be -// allowed) do not interrupt ppoll. -TEST_F(PpollTest, SignalMaskBlocksSignal) { - absl::Duration duration(absl::Seconds(30)); - struct timespec timeout = absl::ToTimespec(duration); - absl::Duration timer_duration(absl::Seconds(10)); - - // Call with a mask that blocks SIGALRM. See that ppoll is not interrupted - // (i.e. returns 0) and that upon completion, the timer has fired. - sigset_t mask; - ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); - TEST_PCHECK(sigaddset(&mask, SIGALRM) == 0); - SetTimer(timer_duration); - MaybeSave(); - ASSERT_FALSE(TimerFired()); - ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, &mask, kSigsetSize), - SyscallSucceeds()); - EXPECT_TRUE(TimerFired()); - EXPECT_EQ(absl::DurationFromTimespec(timeout), absl::Duration()); -} - -// Verify that signals allowed by the ppoll mask (that would otherwise be -// blocked) interrupt ppoll. -TEST_F(PpollTest, SignalMaskAllowsSignal) { - absl::Duration duration(absl::Seconds(30)); - struct timespec timeout = absl::ToTimespec(duration); - absl::Duration timer_duration(absl::Seconds(10)); - - sigset_t mask; - ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); - - // Block SIGALRM. - auto cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGALRM)); - - // Call with a mask that unblocks SIGALRM. See that ppoll is interrupted. - SetTimer(timer_duration); - MaybeSave(); - ASSERT_FALSE(TimerFired()); - ASSERT_THAT(syscallPpoll(nullptr, 0, &timeout, &mask, kSigsetSize), - SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); - EXPECT_GT(absl::DurationFromTimespec(timeout), absl::Duration()); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc deleted file mode 100644 index f675dc430..000000000 --- a/test/syscalls/linux/prctl.cc +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/prctl.h> -#include <sys/ptrace.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(bool, prctl_no_new_privs_test_child, false, - "If true, exit with the return value of prctl(PR_GET_NO_NEW_PRIVS) " - "plus an offset (see test source)."); - -namespace gvisor { -namespace testing { - -namespace { - -#ifndef SUID_DUMP_DISABLE -#define SUID_DUMP_DISABLE 0 -#endif /* SUID_DUMP_DISABLE */ -#ifndef SUID_DUMP_USER -#define SUID_DUMP_USER 1 -#endif /* SUID_DUMP_USER */ -#ifndef SUID_DUMP_ROOT -#define SUID_DUMP_ROOT 2 -#endif /* SUID_DUMP_ROOT */ - -TEST(PrctlTest, NameInitialized) { - const size_t name_length = 20; - char name[name_length] = {}; - ASSERT_THAT(prctl(PR_GET_NAME, name), SyscallSucceeds()); - ASSERT_NE(std::string(name), ""); -} - -TEST(PrctlTest, SetNameLongName) { - const size_t name_length = 20; - const std::string long_name(name_length, 'A'); - ASSERT_THAT(prctl(PR_SET_NAME, long_name.c_str()), SyscallSucceeds()); - char truncated_name[name_length] = {}; - ASSERT_THAT(prctl(PR_GET_NAME, truncated_name), SyscallSucceeds()); - const size_t truncated_length = 15; - ASSERT_EQ(long_name.substr(0, truncated_length), std::string(truncated_name)); -} - -TEST(PrctlTest, ChildProcessName) { - constexpr size_t kMaxNameLength = 15; - - char parent_name[kMaxNameLength + 1] = {}; - memset(parent_name, 'a', kMaxNameLength); - - ASSERT_THAT(prctl(PR_SET_NAME, parent_name), SyscallSucceeds()); - - pid_t child_pid = fork(); - TEST_PCHECK(child_pid >= 0); - if (child_pid == 0) { - char child_name[kMaxNameLength + 1] = {}; - TEST_PCHECK(prctl(PR_GET_NAME, child_name) >= 0); - TEST_CHECK(memcmp(parent_name, child_name, sizeof(parent_name)) == 0); - _exit(0); - } - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status =" << status; -} - -// Offset added to exit code from test child to distinguish from other abnormal -// exits. -constexpr int kPrctlNoNewPrivsTestChildExitBase = 100; - -TEST(PrctlTest, NoNewPrivsPreservedAcrossCloneForkAndExecve) { - // Check if no_new_privs is already set. If it is, we can still test that it's - // preserved across clone/fork/execve, but we also expect it to still be set - // at the end of the test. Otherwise, call prctl(PR_SET_NO_NEW_PRIVS) so as - // not to contaminate the original thread. - int no_new_privs; - ASSERT_THAT(no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), - SyscallSucceeds()); - ScopedThread([] { - ASSERT_THAT(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), SyscallSucceeds()); - EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), - SyscallSucceedsWithValue(1)); - ScopedThread([] { - EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), - SyscallSucceedsWithValue(1)); - // Note that these ASSERT_*s failing will only return from this thread, - // but this is the intended behavior. - pid_t child_pid = -1; - int execve_errno = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/proc/self/exe", - {"/proc/self/exe", "--prctl_no_new_privs_test_child"}, {}, - nullptr, &child_pid, &execve_errno)); - - ASSERT_GT(child_pid, 0); - ASSERT_EQ(execve_errno, 0); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceeds()); - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), kPrctlNoNewPrivsTestChildExitBase + 1); - - EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), - SyscallSucceedsWithValue(1)); - }); - EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), - SyscallSucceedsWithValue(1)); - }); - EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0), - SyscallSucceedsWithValue(no_new_privs)); -} - -TEST(PrctlTest, PDeathSig) { - pid_t child_pid; - - // Make the new process' parent a separate thread since the parent death - // signal fires when the parent *thread* exits. - ScopedThread([&] { - child_pid = fork(); - TEST_CHECK(child_pid >= 0); - if (child_pid == 0) { - // In child process. - TEST_CHECK(prctl(PR_SET_PDEATHSIG, SIGKILL) >= 0); - int signo; - TEST_CHECK(prctl(PR_GET_PDEATHSIG, &signo) >= 0); - TEST_CHECK(signo == SIGKILL); - // Enable tracing, then raise SIGSTOP and expect our parent to suppress - // it. - TEST_CHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) >= 0); - TEST_CHECK(raise(SIGSTOP) == 0); - // Sleep until killed by our parent death signal. sleep(3) is - // async-signal-safe, absl::SleepFor isn't. - while (true) { - sleep(10); - } - } - // In parent process. - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << "status = " << status; - - // Suppress the SIGSTOP and detach from the child. - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - }); - - // The child should have been killed by its parent death SIGKILL. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << "status = " << status; -} - -// This test is to validate that calling prctl with PR_SET_MM without the -// CAP_SYS_RESOURCE returns EPERM. -TEST(PrctlTest, InvalidPrSetMM) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))) { - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_RESOURCE, - false)); // Drop capability to test below. - } - ASSERT_THAT(prctl(PR_SET_MM, 0, 0, 0, 0), SyscallFailsWithErrno(EPERM)); -} - -// Sanity check that dumpability is remembered. -TEST(PrctlTest, SetGetDumpability) { - int before; - ASSERT_THAT(before = prctl(PR_GET_DUMPABLE), SyscallSucceeds()); - auto cleanup = Cleanup([before] { - ASSERT_THAT(prctl(PR_SET_DUMPABLE, before), SyscallSucceeds()); - }); - - EXPECT_THAT(prctl(PR_SET_DUMPABLE, SUID_DUMP_DISABLE), SyscallSucceeds()); - EXPECT_THAT(prctl(PR_GET_DUMPABLE), - SyscallSucceedsWithValue(SUID_DUMP_DISABLE)); - - EXPECT_THAT(prctl(PR_SET_DUMPABLE, SUID_DUMP_USER), SyscallSucceeds()); - EXPECT_THAT(prctl(PR_GET_DUMPABLE), SyscallSucceedsWithValue(SUID_DUMP_USER)); -} - -// SUID_DUMP_ROOT cannot be set via PR_SET_DUMPABLE. -TEST(PrctlTest, RootDumpability) { - EXPECT_THAT(prctl(PR_SET_DUMPABLE, SUID_DUMP_ROOT), - SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_prctl_no_new_privs_test_child)) { - exit(gvisor::testing::kPrctlNoNewPrivsTestChildExitBase + - prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0)); - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/prctl_setuid.cc b/test/syscalls/linux/prctl_setuid.cc deleted file mode 100644 index c4e9cf528..000000000 --- a/test/syscalls/linux/prctl_setuid.cc +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sched.h> -#include <sys/prctl.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "test/util/capability_util.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(int32_t, scratch_uid, 65534, "scratch UID"); -// This flag is used to verify that after an exec PR_GET_KEEPCAPS -// returns 0, the return code will be offset by kPrGetKeepCapsExitBase. -ABSL_FLAG(bool, prctl_pr_get_keepcaps, false, - "If true the test will verify that prctl with pr_get_keepcaps" - "returns 0. The test will exit with the result of that check."); - -// These tests exist seperately from prctl because we need to start -// them as root. Setuid() has the behavior that permissions are fully -// removed if one of the UIDs were 0 before a setuid() call. This -// behavior can be changed by using PR_SET_KEEPCAPS and that is what -// is tested here. -// -// Reference setuid(2): -// The setuid() function checks the effective user ID of -// the caller and if it is the superuser, all process-related user ID's -// are set to uid. After this has occurred, it is impossible for the -// program to regain root privileges. -// -// Thus, a set-user-ID-root program wishing to temporarily drop root -// privileges, assume the identity of an unprivileged user, and then -// regain root privileges afterward cannot use setuid(). You can -// accomplish this with seteuid(2). -namespace gvisor { -namespace testing { - -// Offset added to exit code from test child to distinguish from other abnormal -// exits. -constexpr int kPrGetKeepCapsExitBase = 100; - -namespace { - -class PrctlKeepCapsSetuidTest : public ::testing::Test { - protected: - void SetUp() override { - // PR_GET_KEEPCAPS will only return 0 or 1 (on success). - ASSERT_THAT(original_keepcaps_ = prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), - SyscallSucceeds()); - ASSERT_TRUE(original_keepcaps_ == 0 || original_keepcaps_ == 1); - } - - void TearDown() override { - // Restore PR_SET_KEEPCAPS. - ASSERT_THAT(prctl(PR_SET_KEEPCAPS, original_keepcaps_, 0, 0, 0), - SyscallSucceeds()); - - // Verify that it was restored. - ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), - SyscallSucceedsWithValue(original_keepcaps_)); - } - - // The original keep caps value exposed so tests can use it if they need. - int original_keepcaps_ = 0; -}; - -// This test will verify that a bad value, eg. not 0 or 1 for -// PR_SET_KEEPCAPS will return EINVAL as required by prctl(2). -TEST_F(PrctlKeepCapsSetuidTest, PrctlBadArgsToKeepCaps) { - ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 2, 0, 0, 0), - SyscallFailsWithErrno(EINVAL)); -} - -// This test will verify that a setuid(2) without PR_SET_KEEPCAPS will cause -// all capabilities to be dropped. -TEST_F(PrctlKeepCapsSetuidTest, SetUidNoKeepCaps) { - // getuid(2) never fails. - if (getuid() != 0) { - SKIP_IF(!IsRunningOnGvisor()); - FAIL() << "User is not root on gvisor platform."; - } - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. After - // calling setuid(non-zero-UID), there is no way to get root privileges - // back. - ScopedThread([] { - // Start by verifying we have a capability. - TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); - - // Verify that PR_GET_KEEPCAPS is disabled. - ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), - SyscallSucceedsWithValue(0)); - - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. POSIX threads, however, require that - // all threads have the same UIDs, so using the setuid wrapper sets all - // threads' real UID. - EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)), - SyscallSucceeds()); - - // Verify that we changed uid. - EXPECT_THAT(getuid(), - SyscallSucceedsWithValue(absl::GetFlag(FLAGS_scratch_uid))); - - // Verify we lost the capability in the effective set, this always happens. - TEST_CHECK(!HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); - - // We should have also lost it in the permitted set by the setuid() so - // SetCapability should fail when we try to add it back to the effective set - ASSERT_FALSE(SetCapability(CAP_SYS_ADMIN, true).ok()); - }); -} - -// This test will verify that a setuid with PR_SET_KEEPCAPS will cause -// capabilities to be retained after we switch away from the root user. -TEST_F(PrctlKeepCapsSetuidTest, SetUidKeepCaps) { - // getuid(2) never fails. - if (getuid() != 0) { - SKIP_IF(!IsRunningOnGvisor()); - FAIL() << "User is not root on gvisor platform."; - } - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. After - // calling setuid(non-zero-UID), there is no way to get root privileges - // back. - ScopedThread([] { - // Start by verifying we have a capability. - TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); - - // Set PR_SET_KEEPCAPS. - ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); - - // Verify PR_SET_KEEPCAPS was set before we proceed. - ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), - SyscallSucceedsWithValue(1)); - - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. POSIX threads, however, require that - // all threads have the same UIDs, so using the setuid wrapper sets all - // threads' real UID. - EXPECT_THAT(syscall(SYS_setuid, absl::GetFlag(FLAGS_scratch_uid)), - SyscallSucceeds()); - - // Verify that we changed uid. - EXPECT_THAT(getuid(), - SyscallSucceedsWithValue(absl::GetFlag(FLAGS_scratch_uid))); - - // Verify we lost the capability in the effective set, this always happens. - TEST_CHECK(!HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); - - // We lost the capability in the effective set, but it will still - // exist in the permitted set so we can elevate the capability. - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, true)); - - // Verify we got back the capability in the effective set. - TEST_CHECK(HaveCapability(CAP_SYS_ADMIN).ValueOrDie()); - }); -} - -// This test will verify that PR_SET_KEEPCAPS is not retained -// across an execve. According to prctl(2): -// "The "keep capabilities" value will be reset to 0 on subsequent -// calls to execve(2)." -TEST_F(PrctlKeepCapsSetuidTest, NoKeepCapsAfterExec) { - ASSERT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); - - // Verify PR_SET_KEEPCAPS was set before we proceed. - ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), SyscallSucceedsWithValue(1)); - - pid_t child_pid = -1; - int execve_errno = 0; - // Do an exec and then verify that PR_GET_KEEPCAPS returns 0 - // see the body of main below. - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - "/proc/self/exe", {"/proc/self/exe", "--prctl_pr_get_keepcaps"}, {}, - nullptr, &child_pid, &execve_errno)); - - ASSERT_GT(child_pid, 0); - ASSERT_EQ(execve_errno, 0); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - ASSERT_TRUE(WIFEXITED(status)); - // PR_SET_KEEPCAPS should have been cleared by the exec. - // Success should return gvisor::testing::kPrGetKeepCapsExitBase + 0 - ASSERT_EQ(WEXITSTATUS(status), kPrGetKeepCapsExitBase); -} - -TEST_F(PrctlKeepCapsSetuidTest, NoKeepCapsAfterNewUserNamespace) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - - // Fork to avoid changing the user namespace of the original test process. - pid_t const child_pid = fork(); - - if (child_pid == 0) { - // Verify that the keepcaps flag is set to 0 when we change user namespaces. - TEST_PCHECK(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == 0); - MaybeSave(); - - TEST_PCHECK(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0) == 1); - MaybeSave(); - - TEST_PCHECK(unshare(CLONE_NEWUSER) == 0); - MaybeSave(); - - TEST_PCHECK(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0) == 0); - MaybeSave(); - - _exit(0); - } - - int status; - ASSERT_THAT(child_pid, SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status = " << status; -} - -// This test will verify that PR_SET_KEEPCAPS and PR_GET_KEEPCAPS work correctly -TEST_F(PrctlKeepCapsSetuidTest, PrGetKeepCaps) { - // Set PR_SET_KEEPCAPS to the negation of the original. - ASSERT_THAT(prctl(PR_SET_KEEPCAPS, !original_keepcaps_, 0, 0, 0), - SyscallSucceeds()); - - // Verify it was set. - ASSERT_THAT(prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0), - SyscallSucceedsWithValue(!original_keepcaps_)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_prctl_pr_get_keepcaps)) { - return gvisor::testing::kPrGetKeepCapsExitBase + - prctl(PR_GET_KEEPCAPS, 0, 0, 0, 0); - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/pread64.cc b/test/syscalls/linux/pread64.cc deleted file mode 100644 index c74990ba1..000000000 --- a/test/syscalls/linux/pread64.cc +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <linux/unistd.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class Pread64Test : public ::testing::Test { - void SetUp() override { - name_ = NewTempAbsPath(); - ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_CREAT, 0644)); - } - - void TearDown() override { unlink(name_.c_str()); } - - public: - std::string name_; -}; - -TEST(Pread64TestNoTempFile, BadFileDescriptor) { - char buf[1024]; - EXPECT_THAT(pread64(-1, buf, 1024, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(Pread64Test, ZeroBuffer) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR)); - - char msg[] = "hello world"; - EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0), - SyscallSucceedsWithValue(strlen(msg))); - - char buf[10]; - EXPECT_THAT(pread64(fd.get(), buf, 0, 0), SyscallSucceedsWithValue(0)); -} - -TEST_F(Pread64Test, BadBuffer) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR)); - - char msg[] = "hello world"; - EXPECT_THAT(pwrite64(fd.get(), msg, strlen(msg), 0), - SyscallSucceedsWithValue(strlen(msg))); - - char* bad_buffer = nullptr; - EXPECT_THAT(pread64(fd.get(), bad_buffer, 1024, 0), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(Pread64Test, WriteOnlyNotReadable) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_WRONLY)); - - char buf[1024]; - EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(Pread64Test, Pread64WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - char buf[1024]; - EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(Pread64Test, DirNotReadable) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); - - char buf[1024]; - EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(Pread64Test, BadOffset) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDONLY)); - - char buf[1024]; - EXPECT_THAT(pread64(fd.get(), buf, 1024, -1), SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(Pread64Test, OffsetNotIncremented) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDWR)); - - char msg[] = "hello world"; - EXPECT_THAT(write(fd.get(), msg, strlen(msg)), - SyscallSucceedsWithValue(strlen(msg))); - int offset; - EXPECT_THAT(offset = lseek(fd.get(), 0, SEEK_CUR), SyscallSucceeds()); - - char buf1[1024]; - EXPECT_THAT(pread64(fd.get(), buf1, 1024, 0), - SyscallSucceedsWithValue(strlen(msg))); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(offset)); - - char buf2[1024]; - EXPECT_THAT(pread64(fd.get(), buf2, 1024, 3), - SyscallSucceedsWithValue(strlen(msg) - 3)); - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(offset)); -} - -TEST_F(Pread64Test, EndOfFile) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(name_, O_RDONLY)); - - char buf[1024]; - EXPECT_THAT(pread64(fd.get(), buf, 1024, 0), SyscallSucceedsWithValue(0)); -} - -int memfd_create(const std::string& name, unsigned int flags) { - return syscall(__NR_memfd_create, name.c_str(), flags); -} - -TEST_F(Pread64Test, Overflow) { - int f = memfd_create("negative", 0); - const FileDescriptor fd(f); - - EXPECT_THAT(ftruncate(fd.get(), 0x7fffffffffffffffull), SyscallSucceeds()); - - char buf[10]; - EXPECT_THAT(pread64(fd.get(), buf, sizeof(buf), 0x7fffffffffffffffull), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(Pread64TestNoTempFile, CantReadSocketPair_NoRandomSave) { - int sock_fds[2]; - EXPECT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds), SyscallSucceeds()); - - char buf[1024]; - EXPECT_THAT(pread64(sock_fds[0], buf, 1024, 0), - SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(pread64(sock_fds[1], buf, 1024, 0), - SyscallFailsWithErrno(ESPIPE)); - - EXPECT_THAT(close(sock_fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(sock_fds[1]), SyscallSucceeds()); -} - -TEST(Pread64TestNoTempFile, CantReadPipe) { - char buf[1024]; - - int pipe_fds[2]; - EXPECT_THAT(pipe(pipe_fds), SyscallSucceeds()); - - EXPECT_THAT(pread64(pipe_fds[0], buf, 1024, 0), - SyscallFailsWithErrno(ESPIPE)); - - EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/preadv.cc b/test/syscalls/linux/preadv.cc deleted file mode 100644 index 1c40f0915..000000000 --- a/test/syscalls/linux/preadv.cc +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <atomic> -#include <string> - -#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/memory_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Stress copy-on-write. Attempts to reproduce b/38430174. -TEST(PreadvTest, MMConcurrencyStress) { - // Fill a one-page file with zeroes (the contents don't really matter). - const auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - /* parent = */ GetAbsoluteTestTmpdir(), - /* content = */ std::string(kPageSize, 0), TempPath::kDefaultFileMode)); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Get a one-page private mapping to read to. - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - - // Repeatedly fork in a separate thread to force the mapping to become - // copy-on-write. - std::atomic<bool> done(false); - const ScopedThread t([&] { - while (!done.load()) { - const pid_t pid = fork(); - TEST_CHECK(pid >= 0); - if (pid == 0) { - // In child. The parent was obviously multithreaded, so it's neither - // safe nor necessary to do much more than exit. - syscall(SYS_exit_group, 0); - } - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status = " << status; - } - }); - - // Repeatedly read to the mapping. - struct iovec iov[2]; - iov[0].iov_base = m.ptr(); - iov[0].iov_len = kPageSize / 2; - iov[1].iov_base = reinterpret_cast<void*>(m.addr() + kPageSize / 2); - iov[1].iov_len = kPageSize / 2; - constexpr absl::Duration kTestDuration = absl::Seconds(5); - const absl::Time end = absl::Now() + kTestDuration; - while (absl::Now() < end) { - // Among other causes, save/restore cycles may cause interruptions resulting - // in partial reads, so we don't expect any particular return value. - EXPECT_THAT(RetryEINTR(preadv)(fd.get(), iov, 2, 0), SyscallSucceeds()); - } - - // Stop the other thread. - done.store(true); - - // The test passes if it neither deadlocks nor crashes the OS. -} - -// This test calls preadv with an O_PATH fd. -TEST(PreadvTest, PreadvWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - struct iovec iov; - iov.iov_base = nullptr; - iov.iov_len = 0; - - EXPECT_THAT(preadv(fd.get(), &iov, 1, 0), SyscallFailsWithErrno(EBADF)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/preadv2.cc b/test/syscalls/linux/preadv2.cc deleted file mode 100644 index cb58719c4..000000000 --- a/test/syscalls/linux/preadv2.cc +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/syscalls/linux/file_base.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#ifndef SYS_preadv2 -#if defined(__x86_64__) -#define SYS_preadv2 327 -#elif defined(__aarch64__) -#define SYS_preadv2 286 -#else -#error "Unknown architecture" -#endif -#endif // SYS_preadv2 - -#ifndef RWF_HIPRI -#define RWF_HIPRI 0x1 -#endif // RWF_HIPRI - -constexpr int kBufSize = 1024; - -std::string SetContent() { - std::string content; - for (int i = 0; i < kBufSize; i++) { - content += static_cast<char>((i % 10) + '0'); - } - return content; -} - -ssize_t preadv2(unsigned long fd, const struct iovec* iov, unsigned long iovcnt, - off_t offset, unsigned long flags) { - // syscall on preadv2 does some weird things (see man syscall and search - // preadv2), so we insert a 0 to word align the flags argument on native. - return syscall(SYS_preadv2, fd, iov, iovcnt, offset, 0, flags); -} - -// This test is the base case where we call preadv (no offset, no flags). -TEST(Preadv2Test, TestBaseCall) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - std::string content = SetContent(); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), content, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - std::vector<char> buf(kBufSize); - struct iovec iov[2]; - iov[0].iov_base = buf.data(); - iov[0].iov_len = buf.size() / 2; - iov[1].iov_base = static_cast<char*>(iov[0].iov_base) + (content.size() / 2); - iov[1].iov_len = content.size() / 2; - - EXPECT_THAT(preadv2(fd.get(), iov, /*iovcnt*/ 2, /*offset=*/0, /*flags=*/0), - SyscallSucceedsWithValue(kBufSize)); - - EXPECT_EQ(content, std::string(buf.data(), buf.size())); -} - -// This test is where we call preadv with an offset and no flags. -TEST(Preadv2Test, TestValidPositiveOffset) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - std::string content = SetContent(); - const std::string prefix = "0"; - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), prefix + content, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - std::vector<char> buf(kBufSize, '0'); - struct iovec iov; - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - EXPECT_THAT(preadv2(fd.get(), &iov, /*iovcnt=*/1, /*offset=*/prefix.size(), - /*flags=*/0), - SyscallSucceedsWithValue(kBufSize)); - - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - EXPECT_EQ(content, std::string(buf.data(), buf.size())); -} - -// This test is the base case where we call readv by using -1 as the offset. The -// read should use the file offset, so the test increments it by one prior to -// calling preadv2. -TEST(Preadv2Test, TestNegativeOneOffset) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - std::string content = SetContent(); - const std::string prefix = "231"; - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), prefix + content, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - ASSERT_THAT(lseek(fd.get(), prefix.size(), SEEK_SET), - SyscallSucceedsWithValue(prefix.size())); - - std::vector<char> buf(kBufSize, '0'); - struct iovec iov; - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - EXPECT_THAT(preadv2(fd.get(), &iov, /*iovcnt=*/1, /*offset=*/-1, /*flags=*/0), - SyscallSucceedsWithValue(kBufSize)); - - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(prefix.size() + buf.size())); - - EXPECT_EQ(content, std::string(buf.data(), buf.size())); -} - -// preadv2 requires if the RWF_HIPRI flag is passed, the fd must be opened with -// O_DIRECT. This test implements a correct call with the RWF_HIPRI flag. -TEST(Preadv2Test, TestCallWithRWF_HIPRI) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - std::string content = SetContent(); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), content, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - EXPECT_THAT(fsync(fd.get()), SyscallSucceeds()); - - std::vector<char> buf(kBufSize, '0'); - struct iovec iov; - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - EXPECT_THAT( - preadv2(fd.get(), &iov, /*iovcnt=*/1, /*offset=*/0, /*flags=*/RWF_HIPRI), - SyscallSucceedsWithValue(kBufSize)); - - EXPECT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - EXPECT_EQ(content, std::string(buf.data(), buf.size())); -} -// This test calls preadv2 with an invalid flag. -TEST(Preadv2Test, TestInvalidFlag) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY | O_DIRECT)); - - std::vector<char> buf(kBufSize, '0'); - struct iovec iov; - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - EXPECT_THAT(preadv2(fd.get(), &iov, /*iovcnt=*/1, - /*offset=*/0, /*flags=*/0xF0), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -// This test calls preadv2 with an invalid offset. -TEST(Preadv2Test, TestInvalidOffset) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY | O_DIRECT)); - - auto iov = absl::make_unique<struct iovec[]>(1); - iov[0].iov_base = nullptr; - iov[0].iov_len = 0; - - EXPECT_THAT(preadv2(fd.get(), iov.get(), /*iovcnt=*/1, /*offset=*/-8, - /*flags=*/0), - SyscallFailsWithErrno(EINVAL)); -} - -// This test calls preadv with a file set O_WRONLY. -TEST(Preadv2Test, TestUnreadableFile) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - - auto iov = absl::make_unique<struct iovec[]>(1); - iov[0].iov_base = nullptr; - iov[0].iov_len = 0; - - EXPECT_THAT(preadv2(fd.get(), iov.get(), /*iovcnt=*/1, - /*offset=*/0, /*flags=*/0), - SyscallFailsWithErrno(EBADF)); -} - -// This test calls preadv2 with a file opened with O_PATH. -TEST(Preadv2Test, Preadv2WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - auto iov = absl::make_unique<struct iovec[]>(1); - iov[0].iov_base = nullptr; - iov[0].iov_len = 0; - - EXPECT_THAT(preadv2(fd.get(), iov.get(), /*iovcnt=*/1, /*offset=*/0, - /*flags=*/0), - SyscallFailsWithErrno(EBADF)); -} - -// Calling preadv2 with a non-negative offset calls preadv. Calling preadv with -// an unseekable file is not allowed. A pipe is used for an unseekable file. -TEST(Preadv2Test, TestUnseekableFileInvalid) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - int pipe_fds[2]; - - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - - auto iov = absl::make_unique<struct iovec[]>(1); - iov[0].iov_base = nullptr; - iov[0].iov_len = 0; - - EXPECT_THAT(preadv2(pipe_fds[0], iov.get(), /*iovcnt=*/1, - /*offset=*/2, /*flags=*/0), - SyscallFailsWithErrno(ESPIPE)); - - EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); -} - -TEST(Preadv2Test, TestUnseekableFileValid) { - SKIP_IF(preadv2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - int pipe_fds[2]; - - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - - std::vector<char> content(32, 'X'); - - EXPECT_THAT(write(pipe_fds[1], content.data(), content.size()), - SyscallSucceedsWithValue(content.size())); - - std::vector<char> buf(content.size()); - auto iov = absl::make_unique<struct iovec[]>(1); - iov[0].iov_base = buf.data(); - iov[0].iov_len = buf.size(); - - EXPECT_THAT(preadv2(pipe_fds[0], iov.get(), /*iovcnt=*/1, - /*offset=*/static_cast<off_t>(-1), /*flags=*/0), - SyscallSucceedsWithValue(buf.size())); - - EXPECT_EQ(content, buf); - - EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/priority.cc b/test/syscalls/linux/priority.cc deleted file mode 100644 index 1d9bdfa70..000000000 --- a/test/syscalls/linux/priority.cc +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/resource.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_split.h" -#include "test/util/capability_util.h" -#include "test/util/fs_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// These tests are for both the getpriority(2) and setpriority(2) syscalls -// These tests are very rudimentary because getpriority and setpriority -// have not yet been fully implemented. - -// Getpriority does something -TEST(GetpriorityTest, Implemented) { - // "getpriority() can legitimately return the value -1, it is necessary to - // clear the external variable errno prior to the call" - errno = 0; - EXPECT_THAT(getpriority(PRIO_PROCESS, /*who=*/0), SyscallSucceeds()); -} - -// Invalid which -TEST(GetpriorityTest, InvalidWhich) { - errno = 0; - EXPECT_THAT(getpriority(/*which=*/3, /*who=*/0), - SyscallFailsWithErrno(EINVAL)); -} - -// Process is found when which=PRIO_PROCESS -TEST(GetpriorityTest, ValidWho) { - errno = 0; - EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), SyscallSucceeds()); -} - -// Process is not found when which=PRIO_PROCESS -TEST(GetpriorityTest, InvalidWho) { - errno = 0; - // Flaky, but it's tough to avoid a race condition when finding an unused pid - EXPECT_THAT(getpriority(PRIO_PROCESS, /*who=*/INT_MAX - 1), - SyscallFailsWithErrno(ESRCH)); -} - -// Setpriority does something -TEST(SetpriorityTest, Implemented) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - // No need to clear errno for setpriority(): - // "The setpriority() call returns 0 if there is no error, or -1 if there is" - EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0, /*nice=*/16), - SyscallSucceeds()); -} - -// Invalid which -TEST(Setpriority, InvalidWhich) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0, /*nice=*/16), - SyscallFailsWithErrno(EINVAL)); -} - -// Process is found when which=PRIO_PROCESS -TEST(SetpriorityTest, ValidWho) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/16), - SyscallSucceeds()); -} - -// niceval is within the range [-20, 19] -TEST(SetpriorityTest, InsideRange) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - // Set 0 < niceval < 19 - int nice = 12; - EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), nice), SyscallSucceeds()); - - errno = 0; - EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), - SyscallSucceedsWithValue(nice)); - - // Set -20 < niceval < 0 - nice = -12; - EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), nice), SyscallSucceeds()); - - errno = 0; - EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), - SyscallSucceedsWithValue(nice)); -} - -// Verify that priority/niceness are exposed via /proc/PID/stat. -TEST(SetpriorityTest, NicenessExposedViaProcfs) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - constexpr int kNiceVal = 12; - ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), kNiceVal), SyscallSucceeds()); - - errno = 0; - ASSERT_THAT(getpriority(PRIO_PROCESS, getpid()), - SyscallSucceedsWithValue(kNiceVal)); - - // Now verify we can read that same value via /proc/self/stat. - std::string proc_stat; - ASSERT_NO_ERRNO(GetContents("/proc/self/stat", &proc_stat)); - std::vector<std::string> pieces = absl::StrSplit(proc_stat, ' '); - ASSERT_GT(pieces.size(), 20); - - int niceness_procfs = 0; - ASSERT_TRUE(absl::SimpleAtoi(pieces[18], &niceness_procfs)); - EXPECT_EQ(niceness_procfs, kNiceVal); -} - -// In the kernel's implementation, values outside the range of [-20, 19] are -// truncated to these minimum and maximum values. See -// https://elixir.bootlin.com/linux/v4.4/source/kernel/sys.c#L190 -TEST(SetpriorityTest, OutsideRange) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - // Set niceval > 19 - EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/100), - SyscallSucceeds()); - - errno = 0; - // Test niceval truncated to 19 - EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), - SyscallSucceedsWithValue(/*maxnice=*/19)); - - // Set niceval < -20 - EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/-100), - SyscallSucceeds()); - - errno = 0; - // Test niceval truncated to -20 - EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()), - SyscallSucceedsWithValue(/*minnice=*/-20)); -} - -// Process is not found when which=PRIO_PROCESS -TEST(SetpriorityTest, InvalidWho) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - // Flaky, but it's tough to avoid a race condition when finding an unused pid - EXPECT_THAT(setpriority(PRIO_PROCESS, - /*who=*/INT_MAX - 1, - /*nice=*/16), - SyscallFailsWithErrno(ESRCH)); -} - -// Nice succeeds, correctly modifies (or in this case does not -// modify priority of process -TEST(SetpriorityTest, NiceSucceeds) { - errno = 0; - const int priority_before = getpriority(PRIO_PROCESS, /*who=*/0); - ASSERT_THAT(nice(/*inc=*/0), SyscallSucceeds()); - - // nice(0) should not change priority - EXPECT_EQ(priority_before, getpriority(PRIO_PROCESS, /*who=*/0)); -} - -// Threads resulting from clone() maintain parent's priority -// Changes to child priority do not affect parent's priority -TEST(GetpriorityTest, CloneMaintainsPriority) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE))); - - constexpr int kParentPriority = 16; - constexpr int kChildPriority = 14; - ASSERT_THAT(setpriority(PRIO_PROCESS, getpid(), kParentPriority), - SyscallSucceeds()); - - ScopedThread th([]() { - // Check that priority equals that of parent thread - pid_t my_tid; - EXPECT_THAT(my_tid = syscall(__NR_gettid), SyscallSucceeds()); - EXPECT_THAT(getpriority(PRIO_PROCESS, my_tid), - SyscallSucceedsWithValue(kParentPriority)); - - // Change the child thread's priority - EXPECT_THAT(setpriority(PRIO_PROCESS, my_tid, kChildPriority), - SyscallSucceeds()); - }); - th.Join(); - - // Check that parent's priority reemained the same even though - // the child's priority was altered - EXPECT_EQ(kParentPriority, getpriority(PRIO_PROCESS, syscall(__NR_gettid))); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/priority_execve.cc b/test/syscalls/linux/priority_execve.cc deleted file mode 100644 index 5cb343bad..000000000 --- a/test/syscalls/linux/priority_execve.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> - -int main(int argc, char** argv, char** envp) { - errno = 0; - int prio = getpriority(PRIO_PROCESS, getpid()); - - // NOTE: getpriority() can legitimately return negative values - // in the range [-20, 0). If errno is set, exit with a value that - // could not be reached by a valid priority. Valid exit values - // for the test are in the range [1, 40], so we'll use 0. - if (errno != 0) { - printf("getpriority() failed with errno = %d\n", errno); - exit(0); - } - - // Used by test to verify priority is being maintained through - // calls to execve(). Since prio should always be in the range - // [-20, 19], we offset by 20 so as not to have negative exit codes. - exit(20 - prio); - - return 0; -} diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc deleted file mode 100644 index 493042dfc..000000000 --- a/test/syscalls/linux/proc.cc +++ /dev/null @@ -1,2685 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <elf.h> -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <linux/magic.h> -#include <linux/sem.h> -#include <sched.h> -#include <signal.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/ptrace.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/utsname.h> -#include <syscall.h> -#include <unistd.h> - -#include <algorithm> -#include <atomic> -#include <functional> -#include <iostream> -#include <map> -#include <memory> -#include <ostream> -#include <regex> -#include <string> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/container/node_hash_set.h" -#include "absl/strings/ascii.h" -#include "absl/strings/match.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/synchronization/mutex.h" -#include "absl/synchronization/notification.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/proc_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/time_util.h" -#include "test/util/timer_util.h" - -// NOTE(magi): No, this isn't really a syscall but this is a really simple -// way to get it tested on both gVisor, PTrace and Linux. - -using ::testing::AllOf; -using ::testing::AnyOf; -using ::testing::ContainerEq; -using ::testing::Contains; -using ::testing::ContainsRegex; -using ::testing::Eq; -using ::testing::Gt; -using ::testing::HasSubstr; -using ::testing::IsSupersetOf; -using ::testing::Pair; -using ::testing::UnorderedElementsAre; -using ::testing::UnorderedElementsAreArray; - -// Exported by glibc. -extern char** environ; - -namespace gvisor { -namespace testing { -namespace { - -#ifndef SUID_DUMP_DISABLE -#define SUID_DUMP_DISABLE 0 -#endif /* SUID_DUMP_DISABLE */ -#ifndef SUID_DUMP_USER -#define SUID_DUMP_USER 1 -#endif /* SUID_DUMP_USER */ -#ifndef SUID_DUMP_ROOT -#define SUID_DUMP_ROOT 2 -#endif /* SUID_DUMP_ROOT */ - -#if defined(__x86_64__) || defined(__i386__) -// This list of "required" fields is taken from reading the file -// arch/x86/kernel/cpu/proc.c and seeing which fields will be unconditionally -// printed by the kernel. -static const char* required_fields[] = { - "processor", - "vendor_id", - "cpu family", - "model\t\t:", - "model name", - "stepping", - "cpu MHz", - "fpu\t\t:", - "fpu_exception", - "cpuid level", - "wp", - "bogomips", - "clflush size", - "cache_alignment", - "address sizes", - "power management", -}; -#elif __aarch64__ -// This list of "required" fields is taken from reading the file -// arch/arm64/kernel/cpuinfo.c and seeing which fields will be unconditionally -// printed by the kernel. -static const char* required_fields[] = { - "processor", "BogoMIPS", "Features", "CPU implementer", - "CPU architecture", "CPU variant", "CPU part", "CPU revision", -}; -#else -#error "Unknown architecture" -#endif - -// Takes the subprocess command line and pid. -// If it returns !OK, WithSubprocess returns immediately. -using SubprocessCallback = std::function<PosixError(int)>; - -std::vector<std::string> saved_argv; // NOLINT - -// Helper function to dump /proc/{pid}/status and check the -// state data. State should = "Z" for zombied or "RSD" for -// running, interruptible sleeping (S), or uninterruptible sleep -// (D). -void CompareProcessState(absl::string_view state, int pid) { - auto status_file = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(absl::StrCat("/proc/", pid, "/status"))); - // N.B. POSIX extended regexes don't support shorthand character classes (\w) - // inside of brackets. - EXPECT_THAT(status_file, - ContainsRegex(absl::StrCat("State:.[", state, - R"EOL(]\s+\([a-zA-Z ]+\))EOL"))); -} - -// Run callbacks while a subprocess is running, zombied, and/or exited. -PosixError WithSubprocess(SubprocessCallback const& running, - SubprocessCallback const& zombied, - SubprocessCallback const& exited) { - int pipe_fds[2] = {}; - if (pipe(pipe_fds) < 0) { - return PosixError(errno, "pipe"); - } - - int child_pid = fork(); - if (child_pid < 0) { - return PosixError(errno, "fork"); - } - - if (child_pid == 0) { - close(pipe_fds[0]); // Close the read end. - const DisableSave ds; // Timing issues. - - // Write to the pipe to tell it we're ready. - char buf = 'a'; - int res = 0; - res = WriteFd(pipe_fds[1], &buf, sizeof(buf)); - TEST_CHECK_MSG(res == sizeof(buf), "Write failure in subprocess"); - - while (true) { - SleepSafe(absl::Milliseconds(100)); - } - } - - close(pipe_fds[1]); // Close the write end. - - int status = 0; - auto wait_cleanup = Cleanup([child_pid, &status] { - EXPECT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); - }); - auto kill_cleanup = Cleanup([child_pid] { - EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); - }); - - // Wait for the child. - char buf = 0; - int res = ReadFd(pipe_fds[0], &buf, sizeof(buf)); - if (res < 0) { - return PosixError(errno, "Read from pipe"); - } else if (res == 0) { - return PosixError(EPIPE, "Unable to read from pipe: EOF"); - } - - if (running) { - // The first arg, RSD, refers to a "running process", or a process with a - // state of Running (R), Interruptable Sleep (S) or Uninterruptable - // Sleep (D). - CompareProcessState("RSD", child_pid); - RETURN_IF_ERRNO(running(child_pid)); - } - - // Kill the process. - kill_cleanup.Release()(); - siginfo_t info; - // Wait until the child process has exited (WEXITED flag) but don't - // reap the child (WNOWAIT flag). - EXPECT_THAT(waitid(P_PID, child_pid, &info, WNOWAIT | WEXITED), - SyscallSucceeds()); - - if (zombied) { - // Arg of "Z" refers to a Zombied Process. - CompareProcessState("Z", child_pid); - RETURN_IF_ERRNO(zombied(child_pid)); - } - - // Wait on the process. - wait_cleanup.Release()(); - // If the process is reaped, then then this should return - // with ECHILD. - EXPECT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallFailsWithErrno(ECHILD)); - - if (exited) { - RETURN_IF_ERRNO(exited(child_pid)); - } - - return NoError(); -} - -// Access the file returned by name when a subprocess is running. -PosixError AccessWhileRunning(std::function<std::string(int pid)> name, - int flags, std::function<void(int fd)> access) { - FileDescriptor fd; - return WithSubprocess( - [&](int pid) -> PosixError { - // Running. - ASSIGN_OR_RETURN_ERRNO(fd, Open(name(pid), flags)); - - access(fd.get()); - return NoError(); - }, - nullptr, nullptr); -} - -// Access the file returned by name when the a subprocess is zombied. -PosixError AccessWhileZombied(std::function<std::string(int pid)> name, - int flags, std::function<void(int fd)> access) { - FileDescriptor fd; - return WithSubprocess( - [&](int pid) -> PosixError { - // Running. - ASSIGN_OR_RETURN_ERRNO(fd, Open(name(pid), flags)); - return NoError(); - }, - [&](int pid) -> PosixError { - // Zombied. - access(fd.get()); - return NoError(); - }, - nullptr); -} - -// Access the file returned by name when the a subprocess is exited. -PosixError AccessWhileExited(std::function<std::string(int pid)> name, - int flags, std::function<void(int fd)> access) { - FileDescriptor fd; - return WithSubprocess( - [&](int pid) -> PosixError { - // Running. - ASSIGN_OR_RETURN_ERRNO(fd, Open(name(pid), flags)); - return NoError(); - }, - nullptr, - [&](int pid) -> PosixError { - // Exited. - access(fd.get()); - return NoError(); - }); -} - -// ReadFd(fd=/proc/PID/basename) while PID is running. -int ReadWhileRunning(std::string const& basename, void* buf, size_t count) { - int ret = 0; - int err = 0; - EXPECT_NO_ERRNO(AccessWhileRunning( - [&](int pid) -> std::string { - return absl::StrCat("/proc/", pid, "/", basename); - }, - O_RDONLY, - [&](int fd) { - ret = ReadFd(fd, buf, count); - err = errno; - })); - errno = err; - return ret; -} - -// ReadFd(fd=/proc/PID/basename) while PID is zombied. -int ReadWhileZombied(std::string const& basename, void* buf, size_t count) { - int ret = 0; - int err = 0; - EXPECT_NO_ERRNO(AccessWhileZombied( - [&](int pid) -> std::string { - return absl::StrCat("/proc/", pid, "/", basename); - }, - O_RDONLY, - [&](int fd) { - ret = ReadFd(fd, buf, count); - err = errno; - })); - errno = err; - return ret; -} - -// ReadFd(fd=/proc/PID/basename) while PID is exited. -int ReadWhileExited(std::string const& basename, void* buf, size_t count) { - int ret = 0; - int err = 0; - EXPECT_NO_ERRNO(AccessWhileExited( - [&](int pid) -> std::string { - return absl::StrCat("/proc/", pid, "/", basename); - }, - O_RDONLY, - [&](int fd) { - ret = ReadFd(fd, buf, count); - err = errno; - })); - errno = err; - return ret; -} - -// readlinkat(fd=/proc/PID/, basename) while PID is running. -int ReadlinkWhileRunning(std::string const& basename, char* buf, size_t count) { - int ret = 0; - int err = 0; - EXPECT_NO_ERRNO(AccessWhileRunning( - [&](int pid) -> std::string { return absl::StrCat("/proc/", pid, "/"); }, - O_DIRECTORY, - [&](int fd) { - ret = readlinkat(fd, basename.c_str(), buf, count); - err = errno; - })); - errno = err; - return ret; -} - -// readlinkat(fd=/proc/PID/, basename) while PID is zombied. -int ReadlinkWhileZombied(std::string const& basename, char* buf, size_t count) { - int ret = 0; - int err = 0; - EXPECT_NO_ERRNO(AccessWhileZombied( - [&](int pid) -> std::string { return absl::StrCat("/proc/", pid, "/"); }, - O_DIRECTORY, - [&](int fd) { - ret = readlinkat(fd, basename.c_str(), buf, count); - err = errno; - })); - errno = err; - return ret; -} - -// readlinkat(fd=/proc/PID/, basename) while PID is exited. -int ReadlinkWhileExited(std::string const& basename, char* buf, size_t count) { - int ret = 0; - int err = 0; - EXPECT_NO_ERRNO(AccessWhileExited( - [&](int pid) -> std::string { return absl::StrCat("/proc/", pid, "/"); }, - O_DIRECTORY, - [&](int fd) { - ret = readlinkat(fd, basename.c_str(), buf, count); - err = errno; - })); - errno = err; - return ret; -} - -TEST(ProcTest, NotFoundInRoot) { - struct stat s; - EXPECT_THAT(stat("/proc/foobar", &s), SyscallFailsWithErrno(ENOENT)); -} - -TEST(ProcSelfTest, IsThreadGroupLeader) { - ScopedThread([] { - const pid_t tgid = getpid(); - const pid_t tid = syscall(SYS_gettid); - EXPECT_NE(tgid, tid); - auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self")); - EXPECT_EQ(link, absl::StrCat(tgid)); - }); -} - -TEST(ProcThreadSelfTest, Basic) { - const pid_t tgid = getpid(); - const pid_t tid = syscall(SYS_gettid); - EXPECT_EQ(tgid, tid); - auto link_threadself = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self")); - EXPECT_EQ(link_threadself, absl::StrCat(tgid, "/task/", tid)); - // Just read one file inside thread-self to ensure that the link is valid. - auto link_threadself_exe = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self/exe")); - auto link_procself_exe = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); - EXPECT_EQ(link_threadself_exe, link_procself_exe); -} - -TEST(ProcThreadSelfTest, Thread) { - ScopedThread([] { - const pid_t tgid = getpid(); - const pid_t tid = syscall(SYS_gettid); - EXPECT_NE(tgid, tid); - auto link_threadself = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self")); - - EXPECT_EQ(link_threadself, absl::StrCat(tgid, "/task/", tid)); - // Just read one file inside thread-self to ensure that the link is valid. - auto link_threadself_exe = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/thread-self/exe")); - auto link_procself_exe = - ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); - EXPECT_EQ(link_threadself_exe, link_procself_exe); - // A thread should not have "/proc/<tid>/task". - struct stat s; - EXPECT_THAT(stat("/proc/thread-self/task", &s), - SyscallFailsWithErrno(ENOENT)); - }); -} - -// Returns the /proc/PID/maps entry for the MAP_PRIVATE | MAP_ANONYMOUS mapping -// m with start address addr and length len. -std::string AnonymousMapsEntry(uintptr_t addr, size_t len, int prot) { - return absl::StrCat(absl::Hex(addr, absl::PadSpec::kZeroPad8), "-", - absl::Hex(addr + len, absl::PadSpec::kZeroPad8), " ", - prot & PROT_READ ? "r" : "-", - prot & PROT_WRITE ? "w" : "-", - prot & PROT_EXEC ? "x" : "-", "p 00000000 00:00 0 "); -} - -std::string AnonymousMapsEntryForMapping(const Mapping& m, int prot) { - return AnonymousMapsEntry(m.addr(), m.len(), prot); -} - -PosixErrorOr<std::map<uint64_t, uint64_t>> ReadProcSelfAuxv() { - std::string auxv_file; - RETURN_IF_ERRNO(GetContents("/proc/self/auxv", &auxv_file)); - const Elf64_auxv_t* auxv_data = - reinterpret_cast<const Elf64_auxv_t*>(auxv_file.data()); - std::map<uint64_t, uint64_t> auxv_entries; - for (int i = 0; auxv_data[i].a_type != AT_NULL; i++) { - auto a_type = auxv_data[i].a_type; - EXPECT_EQ(0, auxv_entries.count(a_type)) << "a_type: " << a_type; - auxv_entries.emplace(a_type, auxv_data[i].a_un.a_val); - } - return auxv_entries; -} - -TEST(ProcSelfAuxv, EntryPresence) { - auto auxv_entries = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfAuxv()); - - EXPECT_EQ(auxv_entries.count(AT_ENTRY), 1); - EXPECT_EQ(auxv_entries.count(AT_PHDR), 1); - EXPECT_EQ(auxv_entries.count(AT_PHENT), 1); - EXPECT_EQ(auxv_entries.count(AT_PHNUM), 1); - EXPECT_EQ(auxv_entries.count(AT_BASE), 1); - EXPECT_EQ(auxv_entries.count(AT_UID), 1); - EXPECT_EQ(auxv_entries.count(AT_EUID), 1); - EXPECT_EQ(auxv_entries.count(AT_GID), 1); - EXPECT_EQ(auxv_entries.count(AT_EGID), 1); - EXPECT_EQ(auxv_entries.count(AT_SECURE), 1); - EXPECT_EQ(auxv_entries.count(AT_CLKTCK), 1); - EXPECT_EQ(auxv_entries.count(AT_RANDOM), 1); - EXPECT_EQ(auxv_entries.count(AT_EXECFN), 1); - EXPECT_EQ(auxv_entries.count(AT_PAGESZ), 1); - EXPECT_EQ(auxv_entries.count(AT_SYSINFO_EHDR), 1); -} - -TEST(ProcSelfAuxv, EntryValues) { - auto proc_auxv = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfAuxv()); - - // We need to find the ELF auxiliary vector. The section of memory pointed to - // by envp contains some pointers to non-null pointers, followed by a single - // pointer to a null pointer, followed by the auxiliary vector. - char** envpi = environ; - while (*envpi) { - ++envpi; - } - - const Elf64_auxv_t* envp_auxv = - reinterpret_cast<const Elf64_auxv_t*>(envpi + 1); - int i; - for (i = 0; envp_auxv[i].a_type != AT_NULL; i++) { - auto a_type = envp_auxv[i].a_type; - EXPECT_EQ(proc_auxv.count(a_type), 1); - EXPECT_EQ(proc_auxv[a_type], envp_auxv[i].a_un.a_val) - << "a_type: " << a_type; - } - EXPECT_EQ(i, proc_auxv.size()); -} - -// Just open and read a part of /proc/self/mem, check that we can read an item. -TEST(ProcPidMem, Read) { - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); - char input[] = "hello-world"; - char output[sizeof(input)]; - ASSERT_THAT(pread(memfd.get(), output, sizeof(output), - reinterpret_cast<off_t>(input)), - SyscallSucceedsWithValue(sizeof(input))); - ASSERT_STREQ(input, output); -} - -// Perform read on an unmapped region. -TEST(ProcPidMem, Unmapped) { - // Strategy: map then unmap, so we have a guaranteed unmapped region - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); - Mapping mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - // Fill it with things - memset(mapping.ptr(), 'x', mapping.len()); - char expected = 'x', output; - ASSERT_THAT(pread(memfd.get(), &output, sizeof(output), - reinterpret_cast<off_t>(mapping.ptr())), - SyscallSucceedsWithValue(sizeof(output))); - ASSERT_EQ(expected, output); - - // Unmap region again - ASSERT_THAT(munmap(mapping.ptr(), mapping.len()), SyscallSucceeds()); - - // Now we want EIO error - ASSERT_THAT(pread(memfd.get(), &output, sizeof(output), - reinterpret_cast<off_t>(mapping.ptr())), - SyscallFailsWithErrno(EIO)); -} - -// Perform read repeatedly to verify offset change. -TEST(ProcPidMem, RepeatedRead) { - auto const num_reads = 3; - char expected[] = "01234567890abcdefghijkl"; - char output[sizeof(expected) / num_reads]; - - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); - ASSERT_THAT(lseek(memfd.get(), reinterpret_cast<off_t>(&expected), SEEK_SET), - SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected))); - for (auto i = 0; i < num_reads; i++) { - ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), - SyscallSucceedsWithValue(sizeof(output))); - ASSERT_EQ(strncmp(&expected[i * sizeof(output)], output, sizeof(output)), - 0); - } -} - -// Perform seek operations repeatedly. -TEST(ProcPidMem, RepeatedSeek) { - auto const num_reads = 3; - char expected[] = "01234567890abcdefghijkl"; - char output[sizeof(expected) / num_reads]; - - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); - ASSERT_THAT(lseek(memfd.get(), reinterpret_cast<off_t>(&expected), SEEK_SET), - SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected))); - // Read from start - ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), - SyscallSucceedsWithValue(sizeof(output))); - ASSERT_EQ(strncmp(&expected[0 * sizeof(output)], output, sizeof(output)), 0); - // Skip ahead one read - ASSERT_THAT(lseek(memfd.get(), sizeof(output), SEEK_CUR), - SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected) + - sizeof(output) * 2)); - // Do read again - ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), - SyscallSucceedsWithValue(sizeof(output))); - ASSERT_EQ(strncmp(&expected[2 * sizeof(output)], output, sizeof(output)), 0); - // Skip back three reads - ASSERT_THAT(lseek(memfd.get(), -3 * sizeof(output), SEEK_CUR), - SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected))); - // Do read again - ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), - SyscallSucceedsWithValue(sizeof(output))); - ASSERT_EQ(strncmp(&expected[0 * sizeof(output)], output, sizeof(output)), 0); - // Check that SEEK_END does not work - ASSERT_THAT(lseek(memfd.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); -} - -// Perform read past an allocated memory region. -TEST(ProcPidMem, PartialRead) { - // Strategy: map large region, then do unmap and remap smaller region - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); - - Mapping mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - ASSERT_THAT(munmap(mapping.ptr(), mapping.len()), SyscallSucceeds()); - Mapping smaller_mapping = ASSERT_NO_ERRNO_AND_VALUE( - Mmap(mapping.ptr(), kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); - - // Fill it with things - memset(smaller_mapping.ptr(), 'x', smaller_mapping.len()); - - // Now we want no error - char expected[] = {'x'}; - std::unique_ptr<char[]> output(new char[kPageSize]); - off_t read_offset = - reinterpret_cast<off_t>(smaller_mapping.ptr()) + kPageSize - 1; - ASSERT_THAT( - pread(memfd.get(), output.get(), sizeof(output.get()), read_offset), - SyscallSucceedsWithValue(sizeof(expected))); - // Since output is larger, than expected we have to do manual compare - ASSERT_EQ(expected[0], (output).get()[0]); -} - -// Perform read on /proc/[pid]/mem after exit. -TEST(ProcPidMem, AfterExit) { - int pfd1[2] = {}; - int pfd2[2] = {}; - - char expected[] = "hello-world"; - - ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); - ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); - - // Create child process - pid_t const child_pid = fork(); - if (child_pid == 0) { - // Close reading end of first pipe - close(pfd1[0]); - - // Tell parent about location of input - char ok = 1; - TEST_CHECK(WriteFd(pfd1[1], &ok, sizeof(ok)) == sizeof(ok)); - TEST_PCHECK(close(pfd1[1]) == 0); - - // Close writing end of second pipe - TEST_PCHECK(close(pfd2[1]) == 0); - - // Await parent OK to die - ok = 0; - TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); - - // Close rest pipes - TEST_PCHECK(close(pfd2[0]) == 0); - _exit(0); - } - - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Close writing end of first pipe - EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); - - // Wait for child to be alive and well - char ok = 0; - EXPECT_THAT(ReadFd(pfd1[0], &ok, sizeof(ok)), - SyscallSucceedsWithValue(sizeof(ok))); - // Close reading end of first pipe - EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); - - // Open /proc/pid/mem fd - std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open(mempath, O_RDONLY)); - - // Expect that we can read - char output[sizeof(expected)]; - EXPECT_THAT(pread(memfd.get(), &output, sizeof(output), - reinterpret_cast<off_t>(&expected)), - SyscallSucceedsWithValue(sizeof(output))); - EXPECT_STREQ(expected, output); - - // Tell proc its ok to go - EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); - ok = 1; - EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), - SyscallSucceedsWithValue(sizeof(ok))); - EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); - - // Expect termination - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); - - // Expect that we can't read anymore - EXPECT_THAT(pread(memfd.get(), &output, sizeof(output), - reinterpret_cast<off_t>(&expected)), - SyscallSucceedsWithValue(0)); -} - -// Read from /proc/[pid]/mem with different UID/GID and attached state. -TEST(ProcPidMem, DifferentUserAttached) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_DAC_OVERRIDE))); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_PTRACE))); - - int pfd1[2] = {}; - int pfd2[2] = {}; - - ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); - ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); - - // Create child process - pid_t const child_pid = fork(); - if (child_pid == 0) { - // Close reading end of first pipe - close(pfd1[0]); - - // Tell parent about location of input - char input[] = "hello-world"; - off_t input_location = reinterpret_cast<off_t>(input); - TEST_CHECK(WriteFd(pfd1[1], &input_location, sizeof(input_location)) == - sizeof(input_location)); - TEST_PCHECK(close(pfd1[1]) == 0); - - // Close writing end of second pipe - TEST_PCHECK(close(pfd2[1]) == 0); - - // Await parent OK to die - char ok = 0; - TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); - - // Close rest pipes - TEST_PCHECK(close(pfd2[0]) == 0); - _exit(0); - } - - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Close writing end of first pipe - EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); - - // Read target location from child - off_t target_location; - EXPECT_THAT(ReadFd(pfd1[0], &target_location, sizeof(target_location)), - SyscallSucceedsWithValue(sizeof(target_location))); - // Close reading end of first pipe - EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); - - ScopedThread([&] { - // Attach to child subprocess without stopping it - EXPECT_THAT(ptrace(PTRACE_SEIZE, child_pid, NULL, NULL), SyscallSucceeds()); - - // Keep capabilities after setuid - EXPECT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); - constexpr int kNobody = 65534; - EXPECT_THAT(syscall(SYS_setuid, kNobody), SyscallSucceeds()); - - // Only restore CAP_SYS_PTRACE and CAP_DAC_OVERRIDE - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, true)); - EXPECT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, true)); - - // Open /proc/pid/mem fd - std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open(mempath, O_RDONLY)); - char expected[] = "hello-world"; - char output[sizeof(expected)]; - EXPECT_THAT(pread(memfd.get(), output, sizeof(output), - reinterpret_cast<off_t>(target_location)), - SyscallSucceedsWithValue(sizeof(output))); - EXPECT_STREQ(expected, output); - - // Tell proc its ok to go - EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); - char ok = 1; - EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), - SyscallSucceedsWithValue(sizeof(ok))); - EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); - - // Expect termination - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - }); -} - -// Attempt to read from /proc/[pid]/mem with different UID/GID. -TEST(ProcPidMem, DifferentUser) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - int pfd1[2] = {}; - int pfd2[2] = {}; - - ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); - ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); - - // Create child process - pid_t const child_pid = fork(); - if (child_pid == 0) { - // Close reading end of first pipe - close(pfd1[0]); - - // Tell parent about location of input - char input[] = "hello-world"; - off_t input_location = reinterpret_cast<off_t>(input); - TEST_CHECK(WriteFd(pfd1[1], &input_location, sizeof(input_location)) == - sizeof(input_location)); - TEST_PCHECK(close(pfd1[1]) == 0); - - // Close writing end of second pipe - TEST_PCHECK(close(pfd2[1]) == 0); - - // Await parent OK to die - char ok = 0; - TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); - - // Close rest pipes - TEST_PCHECK(close(pfd2[0]) == 0); - _exit(0); - } - - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Close writing end of first pipe - EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); - - // Read target location from child - off_t target_location; - EXPECT_THAT(ReadFd(pfd1[0], &target_location, sizeof(target_location)), - SyscallSucceedsWithValue(sizeof(target_location))); - // Close reading end of first pipe - EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); - - ScopedThread([&] { - constexpr int kNobody = 65534; - EXPECT_THAT(syscall(SYS_setuid, kNobody), SyscallSucceeds()); - - // Attempt to open /proc/[child_pid]/mem - std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); - EXPECT_THAT(open(mempath.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); - - // Tell proc its ok to go - EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); - char ok = 1; - EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), - SyscallSucceedsWithValue(sizeof(ok))); - EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); - - // Expect termination - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); - }); -} - -// Perform read on /proc/[pid]/mem with same UID/GID. -TEST(ProcPidMem, SameUser) { - int pfd1[2] = {}; - int pfd2[2] = {}; - - ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); - ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); - - // Create child process - pid_t const child_pid = fork(); - if (child_pid == 0) { - // Close reading end of first pipe - close(pfd1[0]); - - // Tell parent about location of input - char input[] = "hello-world"; - off_t input_location = reinterpret_cast<off_t>(input); - TEST_CHECK(WriteFd(pfd1[1], &input_location, sizeof(input_location)) == - sizeof(input_location)); - TEST_PCHECK(close(pfd1[1]) == 0); - - // Close writing end of second pipe - TEST_PCHECK(close(pfd2[1]) == 0); - - // Await parent OK to die - char ok = 0; - TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); - - // Close rest pipes - TEST_PCHECK(close(pfd2[0]) == 0); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Close writing end of first pipe - EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); - - // Read target location from child - off_t target_location; - EXPECT_THAT(ReadFd(pfd1[0], &target_location, sizeof(target_location)), - SyscallSucceedsWithValue(sizeof(target_location))); - // Close reading end of first pipe - EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); - - // Open /proc/pid/mem fd - std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); - auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open(mempath, O_RDONLY)); - char expected[] = "hello-world"; - char output[sizeof(expected)]; - EXPECT_THAT(pread(memfd.get(), output, sizeof(output), - reinterpret_cast<off_t>(target_location)), - SyscallSucceedsWithValue(sizeof(output))); - EXPECT_STREQ(expected, output); - - // Tell proc its ok to go - EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); - char ok = 1; - EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), - SyscallSucceedsWithValue(sizeof(ok))); - EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); - - // Expect termination - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); -} - -// Just open and read /proc/self/maps, check that we can find [stack] -TEST(ProcSelfMaps, Basic) { - auto proc_self_maps = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - - std::vector<std::string> strings = absl::StrSplit(proc_self_maps, '\n'); - std::vector<std::string> stacks; - // Make sure there's a stack in there. - for (const auto& str : strings) { - if (str.find("[stack]") != std::string::npos) { - stacks.push_back(str); - } - } - ASSERT_EQ(1, stacks.size()) << "[stack] not found in: " << proc_self_maps; - // Linux pads to 73 characters then we add 7. - EXPECT_EQ(80, stacks[0].length()); -} - -TEST(ProcSelfMaps, Map1) { - Mapping mapping = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_READ, MAP_PRIVATE)); - auto proc_self_maps = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - std::vector<std::string> strings = absl::StrSplit(proc_self_maps, '\n'); - std::vector<std::string> addrs; - // Make sure if is listed. - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(mapping, PROT_READ)) { - addrs.push_back(str); - } - } - ASSERT_EQ(1, addrs.size()); -} - -TEST(ProcSelfMaps, Map2) { - // NOTE(magi): The permissions must be different or the pages will get merged. - Mapping map1 = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_EXEC, MAP_PRIVATE)); - Mapping map2 = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_WRITE, MAP_PRIVATE)); - - auto proc_self_maps = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - std::vector<std::string> strings = absl::StrSplit(proc_self_maps, '\n'); - std::vector<std::string> addrs; - // Make sure if is listed. - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(map1, PROT_READ | PROT_EXEC)) { - addrs.push_back(str); - } - } - ASSERT_EQ(1, addrs.size()); - addrs.clear(); - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(map2, PROT_WRITE)) { - addrs.push_back(str); - } - } - ASSERT_EQ(1, addrs.size()); -} - -TEST(ProcSelfMaps, MapUnmap) { - Mapping map1 = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ | PROT_EXEC, MAP_PRIVATE)); - Mapping map2 = - ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_WRITE, MAP_PRIVATE)); - - auto proc_self_maps = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - std::vector<std::string> strings = absl::StrSplit(proc_self_maps, '\n'); - std::vector<std::string> addrs; - // Make sure if is listed. - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(map1, PROT_READ | PROT_EXEC)) { - addrs.push_back(str); - } - } - ASSERT_EQ(1, addrs.size()) << proc_self_maps; - addrs.clear(); - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(map2, PROT_WRITE)) { - addrs.push_back(str); - } - } - ASSERT_EQ(1, addrs.size()); - - map2.reset(); - - // Read it again. - proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - strings = absl::StrSplit(proc_self_maps, '\n'); - // First entry should be there. - addrs.clear(); - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(map1, PROT_READ | PROT_EXEC)) { - addrs.push_back(str); - } - } - ASSERT_EQ(1, addrs.size()); - addrs.clear(); - // But not the second. - for (const auto& str : strings) { - if (str == AnonymousMapsEntryForMapping(map2, PROT_WRITE)) { - addrs.push_back(str); - } - } - ASSERT_EQ(0, addrs.size()); -} - -TEST(ProcSelfMaps, Mprotect) { - // FIXME(jamieliu): Linux's mprotect() sometimes fails to merge VMAs in this - // case. - SKIP_IF(!IsRunningOnGvisor()); - - // Reserve 5 pages of address space. - Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(5 * kPageSize, PROT_NONE, MAP_PRIVATE)); - - // Change the permissions on the middle 3 pages. (The first and last pages may - // be merged with other vmas on either side, so they aren't tested directly; - // they just ensure that the middle 3 pages are bracketed by VMAs with - // incompatible permissions.) - ASSERT_THAT(mprotect(reinterpret_cast<void*>(m.addr() + kPageSize), - 3 * kPageSize, PROT_READ), - SyscallSucceeds()); - - // Check that the middle 3 pages make up a single VMA. - auto proc_self_maps = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - std::vector<std::string> strings = absl::StrSplit(proc_self_maps, '\n'); - EXPECT_THAT(strings, Contains(AnonymousMapsEntry(m.addr() + kPageSize, - 3 * kPageSize, PROT_READ))); - - // Change the permissions on the middle page only. - ASSERT_THAT(mprotect(reinterpret_cast<void*>(m.addr() + 2 * kPageSize), - kPageSize, PROT_READ | PROT_WRITE), - SyscallSucceeds()); - - // Check that the single VMA has been split into 3 VMAs. - proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - strings = absl::StrSplit(proc_self_maps, '\n'); - EXPECT_THAT( - strings, - IsSupersetOf( - {AnonymousMapsEntry(m.addr() + kPageSize, kPageSize, PROT_READ), - AnonymousMapsEntry(m.addr() + 2 * kPageSize, kPageSize, - PROT_READ | PROT_WRITE), - AnonymousMapsEntry(m.addr() + 3 * kPageSize, kPageSize, - PROT_READ)})); - - // Change the permissions on the middle page back. - ASSERT_THAT(mprotect(reinterpret_cast<void*>(m.addr() + 2 * kPageSize), - kPageSize, PROT_READ), - SyscallSucceeds()); - - // Check that the 3 VMAs have been merged back into a single VMA. - proc_self_maps = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - strings = absl::StrSplit(proc_self_maps, '\n'); - EXPECT_THAT(strings, Contains(AnonymousMapsEntry(m.addr() + kPageSize, - 3 * kPageSize, PROT_READ))); -} - -TEST(ProcSelfMaps, SharedAnon) { - const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kPageSize, PROT_READ, MAP_SHARED | MAP_ANONYMOUS)); - - const auto proc_self_maps = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - for (const auto& line : absl::StrSplit(proc_self_maps, '\n')) { - const auto entry = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMapsLine(line)); - if (entry.start <= m.addr() && m.addr() < entry.end) { - // cf. proc(5), "/proc/[pid]/map_files/" - EXPECT_EQ(entry.filename, "/dev/zero (deleted)"); - return; - } - } - FAIL() << "no maps entry containing mapping at " << m.ptr(); -} - -TEST(ProcSelfFd, OpenFd) { - int pipe_fds[2]; - ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds()); - - // Reopen the write end. - const std::string path = absl::StrCat("/proc/self/fd/", pipe_fds[1]); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_WRONLY)); - - // Ensure that a read/write works. - const std::string data = "hello"; - std::unique_ptr<char[]> buffer(new char[data.size()]); - EXPECT_THAT(write(fd.get(), data.c_str(), data.size()), - SyscallSucceedsWithValue(5)); - EXPECT_THAT(read(pipe_fds[0], buffer.get(), data.size()), - SyscallSucceedsWithValue(5)); - EXPECT_EQ(strncmp(buffer.get(), data.c_str(), data.size()), 0); - - // Cleanup. - ASSERT_THAT(close(pipe_fds[0]), SyscallSucceeds()); - ASSERT_THAT(close(pipe_fds[1]), SyscallSucceeds()); -} - -static void CheckFdDirGetdentsDuplicates(const std::string& path) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.c_str(), O_RDONLY | O_DIRECTORY)); - // Open a FD whose value is supposed to be much larger than - // the number of FDs opened by current process. - auto newfd = fcntl(fd.get(), F_DUPFD, 1024); - EXPECT_GE(newfd, 1024); - auto fd_closer = Cleanup([newfd]() { close(newfd); }); - auto fd_files = ASSERT_NO_ERRNO_AND_VALUE(ListDir(path.c_str(), false)); - absl::node_hash_set<std::string> fd_files_dedup(fd_files.begin(), - fd_files.end()); - EXPECT_EQ(fd_files.size(), fd_files_dedup.size()); -} - -// This is a regression test for gvisor.dev/issues/3894 -TEST(ProcSelfFd, GetdentsDuplicates) { - CheckFdDirGetdentsDuplicates("/proc/self/fd"); -} - -// This is a regression test for gvisor.dev/issues/3894 -TEST(ProcSelfFdInfo, GetdentsDuplicates) { - CheckFdDirGetdentsDuplicates("/proc/self/fdinfo"); -} - -TEST(ProcSelfFdInfo, CorrectFds) { - // Make sure there is at least one open file. - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); - - // Get files in /proc/self/fd. - auto fd_files = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/fd", false)); - - // Get files in /proc/self/fdinfo. - auto fdinfo_files = - ASSERT_NO_ERRNO_AND_VALUE(ListDir("/proc/self/fdinfo", false)); - - // They should contain the same fds. - EXPECT_THAT(fd_files, UnorderedElementsAreArray(fdinfo_files)); - - // Both should contain fd. - auto fd_s = absl::StrCat(fd.get()); - EXPECT_THAT(fd_files, Contains(fd_s)); -} - -TEST(ProcSelfFdInfo, Flags) { - std::string path = NewTempAbsPath(); - - // Create file here with O_CREAT to test that O_CREAT does not appear in - // fdinfo flags. - int flags = O_CREAT | O_RDWR | O_APPEND | O_CLOEXEC; - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path, flags, 0644)); - - // Automatically delete path. - TempPath temp_path(path); - - // O_CREAT does not appear in fdinfo flags. - flags &= ~O_CREAT; - - // O_LARGEFILE always appears (on x86_64). - flags |= kOLargeFile; - - auto fd_info = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(absl::StrCat("/proc/self/fdinfo/", fd.get()))); - EXPECT_THAT(fd_info, HasSubstr(absl::StrFormat("flags:\t%#o", flags))); -} - -TEST(ProcSelfExe, Absolute) { - auto exe = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); - EXPECT_EQ(exe[0], '/'); -} - -TEST(ProcSelfCwd, Absolute) { - auto exe = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/cwd")); - EXPECT_EQ(exe[0], '/'); -} - -// Sanity check for /proc/cpuinfo fields that must be present. -TEST(ProcCpuinfo, RequiredFieldsArePresent) { - std::string proc_cpuinfo = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/cpuinfo")); - ASSERT_FALSE(proc_cpuinfo.empty()); - std::vector<std::string> cpuinfo_fields = absl::StrSplit(proc_cpuinfo, '\n'); - - // Check that the usual fields are there. We don't really care about the - // contents. - for (const std::string& field : required_fields) { - EXPECT_THAT(proc_cpuinfo, HasSubstr(field)); - } -} - -TEST(ProcCpuinfo, DeniesWriteNonRoot) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_FOWNER))); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting this - // test. Otherwise, the files are created by root (UID before the test), but - // cannot be opened by the `uid` set below after the test. After calling - // setuid(non-zero-UID), there is no way to get root privileges back. - ScopedThread([&] { - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. POSIX threads, however, require that all - // threads have the same UIDs, so using the setuid wrapper sets all threads' - // real UID. - // Also drops capabilities. - constexpr int kNobody = 65534; - EXPECT_THAT(syscall(SYS_setuid, kNobody), SyscallSucceeds()); - EXPECT_THAT(open("/proc/cpuinfo", O_WRONLY), SyscallFailsWithErrno(EACCES)); - EXPECT_THAT(truncate("/proc/cpuinfo", 123), SyscallFailsWithErrno(EACCES)); - }); -} - -// With root privileges, it is possible to open /proc/cpuinfo with write mode, -// but all write operations should fail. -TEST(ProcCpuinfo, DeniesWriteRoot) { - // VFS1 does not behave differently for root/non-root. - SKIP_IF(IsRunningWithVFS1()); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_FOWNER))); - - int fd; - EXPECT_THAT(fd = open("/proc/cpuinfo", O_WRONLY), SyscallSucceeds()); - if (fd > 0) { - // Truncate is not tested--it may succeed on some kernels without doing - // anything. - EXPECT_THAT(write(fd, "x", 1), SyscallFails()); - EXPECT_THAT(pwrite(fd, "x", 1, 123), SyscallFails()); - } -} - -// Sanity checks that uptime is present. -TEST(ProcUptime, IsPresent) { - std::string proc_uptime = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/uptime")); - ASSERT_FALSE(proc_uptime.empty()); - std::vector<std::string> uptime_parts = absl::StrSplit(proc_uptime, ' '); - - // Parse once. - double uptime0, uptime1, idletime0, idletime1; - ASSERT_TRUE(absl::SimpleAtod(uptime_parts[0], &uptime0)); - ASSERT_TRUE(absl::SimpleAtod(uptime_parts[1], &idletime0)); - - // Sleep for one second. - absl::SleepFor(absl::Seconds(1)); - - // Parse again. - proc_uptime = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/uptime")); - ASSERT_FALSE(proc_uptime.empty()); - uptime_parts = absl::StrSplit(proc_uptime, ' '); - ASSERT_TRUE(absl::SimpleAtod(uptime_parts[0], &uptime1)); - ASSERT_TRUE(absl::SimpleAtod(uptime_parts[1], &idletime1)); - - // Sanity check. - // - // We assert that between 0.99 and 59.99 seconds have passed. If more than a - // minute has passed, then we must be executing really, really slowly. - EXPECT_GE(uptime0, 0.0); - EXPECT_GE(idletime0, 0.0); - EXPECT_GT(uptime1, uptime0); - EXPECT_GE(uptime1, uptime0 + 0.99); - EXPECT_LE(uptime1, uptime0 + 59.99); - EXPECT_GE(idletime1, idletime0); -} - -TEST(ProcMeminfo, ContainsBasicFields) { - std::string proc_meminfo = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/meminfo")); - EXPECT_THAT(proc_meminfo, AllOf(ContainsRegex(R"(MemTotal:\s+[0-9]+ kB)"), - ContainsRegex(R"(MemFree:\s+[0-9]+ kB)"))); -} - -TEST(ProcStat, ContainsBasicFields) { - std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); - - std::vector<std::string> names; - for (auto const& line : absl::StrSplit(proc_stat, '\n')) { - std::vector<std::string> fields = - absl::StrSplit(line, ' ', absl::SkipWhitespace()); - if (fields.empty()) { - continue; - } - names.push_back(fields[0]); - } - - EXPECT_THAT(names, - IsSupersetOf({"cpu", "intr", "ctxt", "btime", "processes", - "procs_running", "procs_blocked", "softirq"})); -} - -TEST(ProcStat, EndsWithNewline) { - std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); - EXPECT_EQ(proc_stat.back(), '\n'); -} - -TEST(ProcStat, Fields) { - std::string proc_stat = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/stat")); - - std::vector<std::string> names; - for (auto const& line : absl::StrSplit(proc_stat, '\n')) { - std::vector<std::string> fields = - absl::StrSplit(line, ' ', absl::SkipWhitespace()); - if (fields.empty()) { - continue; - } - - if (absl::StartsWith(fields[0], "cpu")) { - // As of Linux 3.11, each CPU entry has 10 fields, plus the name. - EXPECT_GE(fields.size(), 11) << proc_stat; - } else if (fields[0] == "ctxt") { - // Single field. - EXPECT_EQ(fields.size(), 2) << proc_stat; - } else if (fields[0] == "btime") { - // Single field. - EXPECT_EQ(fields.size(), 2) << proc_stat; - } else if (fields[0] == "itime") { - // Single field. - ASSERT_EQ(fields.size(), 2) << proc_stat; - // This is the only floating point field. - double val; - EXPECT_TRUE(absl::SimpleAtod(fields[1], &val)) << proc_stat; - continue; - } else if (fields[0] == "processes") { - // Single field. - EXPECT_EQ(fields.size(), 2) << proc_stat; - } else if (fields[0] == "procs_running") { - // Single field. - EXPECT_EQ(fields.size(), 2) << proc_stat; - } else if (fields[0] == "procs_blocked") { - // Single field. - EXPECT_EQ(fields.size(), 2) << proc_stat; - } else if (fields[0] == "softirq") { - // As of Linux 3.11, there are 10 softirqs. 12 fields for name + total. - EXPECT_GE(fields.size(), 12) << proc_stat; - } - - // All fields besides itime are valid base 10 numbers. - for (size_t i = 1; i < fields.size(); i++) { - uint64_t val; - EXPECT_TRUE(absl::SimpleAtoi(fields[i], &val)) << proc_stat; - } - } -} - -TEST(ProcLoadavg, EndsWithNewline) { - std::string proc_loadvg = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/loadavg")); - EXPECT_EQ(proc_loadvg.back(), '\n'); -} - -TEST(ProcLoadavg, Fields) { - std::string proc_loadvg = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/loadavg")); - std::vector<std::string> lines = absl::StrSplit(proc_loadvg, '\n'); - - // Single line. - EXPECT_EQ(lines.size(), 2) << proc_loadvg; - - std::vector<std::string> fields = - absl::StrSplit(lines[0], absl::ByAnyChar(" /"), absl::SkipWhitespace()); - - // Six fields. - EXPECT_EQ(fields.size(), 6) << proc_loadvg; - - double val; - uint64_t val2; - // First three fields are floating point numbers. - EXPECT_TRUE(absl::SimpleAtod(fields[0], &val)) << proc_loadvg; - EXPECT_TRUE(absl::SimpleAtod(fields[1], &val)) << proc_loadvg; - EXPECT_TRUE(absl::SimpleAtod(fields[2], &val)) << proc_loadvg; - // Rest of the fields are valid base 10 numbers. - EXPECT_TRUE(absl::SimpleAtoi(fields[3], &val2)) << proc_loadvg; - EXPECT_TRUE(absl::SimpleAtoi(fields[4], &val2)) << proc_loadvg; - EXPECT_TRUE(absl::SimpleAtoi(fields[5], &val2)) << proc_loadvg; -} - -// NOTE: Tests in priority.cc also check certain priority related fields in -// /proc/self/stat. - -class ProcPidStatTest : public ::testing::TestWithParam<std::string> {}; - -TEST_P(ProcPidStatTest, HasBasicFields) { - std::string proc_pid_stat = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(absl::StrCat("/proc/", GetParam(), "/stat"))); - - ASSERT_FALSE(proc_pid_stat.empty()); - std::vector<std::string> fields = absl::StrSplit(proc_pid_stat, ' '); - ASSERT_GE(fields.size(), 24); - EXPECT_EQ(absl::StrCat(getpid()), fields[0]); - // fields[1] is the thread name. - EXPECT_EQ("R", fields[2]); // task state - EXPECT_EQ(absl::StrCat(getppid()), fields[3]); - - // If the test starts up quickly, then the process start time and the kernel - // boot time will be very close, and the proc starttime field (which is the - // delta of the two times) will be 0. For that unfortunate reason, we can - // only check that starttime >= 0, and not that it is strictly > 0. - uint64_t starttime; - ASSERT_TRUE(absl::SimpleAtoi(fields[21], &starttime)); - EXPECT_GE(starttime, 0); - - uint64_t vss; - ASSERT_TRUE(absl::SimpleAtoi(fields[22], &vss)); - EXPECT_GT(vss, 0); - - uint64_t rss; - ASSERT_TRUE(absl::SimpleAtoi(fields[23], &rss)); - EXPECT_GT(rss, 0); - - uint64_t rsslim; - ASSERT_TRUE(absl::SimpleAtoi(fields[24], &rsslim)); - EXPECT_GT(rsslim, 0); -} - -INSTANTIATE_TEST_SUITE_P(SelfAndNumericPid, ProcPidStatTest, - ::testing::Values("self", absl::StrCat(getpid()))); - -using ProcPidStatmTest = ::testing::TestWithParam<std::string>; - -TEST_P(ProcPidStatmTest, HasBasicFields) { - std::string proc_pid_statm = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(absl::StrCat("/proc/", GetParam(), "/statm"))); - ASSERT_FALSE(proc_pid_statm.empty()); - std::vector<std::string> fields = absl::StrSplit(proc_pid_statm, ' '); - ASSERT_GE(fields.size(), 7); - - uint64_t vss; - ASSERT_TRUE(absl::SimpleAtoi(fields[0], &vss)); - EXPECT_GT(vss, 0); - - uint64_t rss; - ASSERT_TRUE(absl::SimpleAtoi(fields[1], &rss)); - EXPECT_GT(rss, 0); -} - -INSTANTIATE_TEST_SUITE_P(SelfAndNumericPid, ProcPidStatmTest, - ::testing::Values("self", absl::StrCat(getpid()))); - -PosixErrorOr<uint64_t> CurrentRSS() { - ASSIGN_OR_RETURN_ERRNO(auto proc_self_stat, GetContents("/proc/self/stat")); - if (proc_self_stat.empty()) { - return PosixError(EINVAL, "empty /proc/self/stat"); - } - - std::vector<std::string> fields = absl::StrSplit(proc_self_stat, ' '); - if (fields.size() < 24) { - return PosixError( - EINVAL, - absl::StrCat("/proc/self/stat has too few fields: ", proc_self_stat)); - } - - uint64_t rss; - if (!absl::SimpleAtoi(fields[23], &rss)) { - return PosixError( - EINVAL, absl::StrCat("/proc/self/stat RSS field is not a number: ", - fields[23])); - } - - // RSS is given in number of pages. - return rss * kPageSize; -} - -// The size of mapping created by MapPopulateRSS. -constexpr uint64_t kMappingSize = 100 << 20; - -// Tolerance on RSS comparisons to account for background thread mappings, -// reclaimed pages, newly faulted pages, etc. -constexpr uint64_t kRSSTolerance = 10 << 20; - -// Capture RSS before and after an anonymous mapping with passed prot. -void MapPopulateRSS(int prot, uint64_t* before, uint64_t* after) { - *before = ASSERT_NO_ERRNO_AND_VALUE(CurrentRSS()); - - // N.B. The kernel asynchronously accumulates per-task RSS counters into the - // mm RSS, which is exposed by /proc/PID/stat. Task exit is a synchronization - // point (kernel/exit.c:do_exit -> sync_mm_rss), so perform the mapping on - // another thread to ensure it is reflected in RSS after the thread exits. - Mapping mapping; - ScopedThread t([&mapping, prot] { - mapping = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(kMappingSize, prot, MAP_PRIVATE | MAP_POPULATE)); - }); - t.Join(); - - *after = ASSERT_NO_ERRNO_AND_VALUE(CurrentRSS()); -} - -// TODO(b/73896574): Test for PROT_READ + MAP_POPULATE anonymous mappings. Their -// semantics are more subtle: -// -// Small pages -> Zero page mapped, not counted in RSS -// (mm/memory.c:do_anonymous_page). -// -// Huge pages (THP enabled, use_zero_page=0) -> Pages committed -// (mm/memory.c:__handle_mm_fault -> create_huge_pmd). -// -// Huge pages (THP enabled, use_zero_page=1) -> Zero page mapped, not counted in -// RSS (mm/huge_memory.c:do_huge_pmd_anonymous_page). - -// PROT_WRITE + MAP_POPULATE anonymous mappings are always committed. -TEST(ProcSelfStat, PopulateWriteRSS) { - uint64_t before, after; - MapPopulateRSS(PROT_READ | PROT_WRITE, &before, &after); - - // Mapping is committed. - EXPECT_NEAR(before + kMappingSize, after, kRSSTolerance); -} - -// PROT_NONE + MAP_POPULATE anonymous mappings are never committed. -TEST(ProcSelfStat, PopulateNoneRSS) { - uint64_t before, after; - MapPopulateRSS(PROT_NONE, &before, &after); - - // Mapping not committed. - EXPECT_NEAR(before, after, kRSSTolerance); -} - -// Returns the calling thread's name. -PosixErrorOr<std::string> ThreadName() { - // "The buffer should allow space for up to 16 bytes; the returned std::string - // will be null-terminated if it is shorter than that." - prctl(2). But we - // always want the thread name to be null-terminated. - char thread_name[17]; - int rc = prctl(PR_GET_NAME, thread_name, 0, 0, 0); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "prctl(PR_GET_NAME)"); - } - thread_name[16] = '\0'; - return std::string(thread_name); -} - -// Parses the contents of a /proc/[pid]/status file into a collection of -// key-value pairs. -PosixErrorOr<std::map<std::string, std::string>> ParseProcStatus( - absl::string_view status_str) { - std::map<std::string, std::string> fields; - for (absl::string_view const line : - absl::StrSplit(status_str, '\n', absl::SkipWhitespace())) { - const std::pair<absl::string_view, absl::string_view> kv = - absl::StrSplit(line, absl::MaxSplits(":\t", 1)); - if (kv.first.empty()) { - return PosixError( - EINVAL, absl::StrCat("failed to parse key in line \"", line, "\"")); - } - std::string key(kv.first); - if (fields.count(key)) { - return PosixError(EINVAL, - absl::StrCat("duplicate key \"", kv.first, "\"")); - } - std::string value(kv.second); - absl::StripLeadingAsciiWhitespace(&value); - fields.emplace(std::move(key), std::move(value)); - } - return fields; -} - -TEST(ParseProcStatusTest, ParsesSimpleStatusFileWithMixedWhitespaceCorrectly) { - EXPECT_THAT( - ParseProcStatus( - "Name:\tinit\nState:\tS (sleeping)\nCapEff:\t 0000001fffffffff\n"), - IsPosixErrorOkAndHolds(UnorderedElementsAre( - Pair("Name", "init"), Pair("State", "S (sleeping)"), - Pair("CapEff", "0000001fffffffff")))); -} - -TEST(ParseProcStatusTest, DetectsDuplicateKeys) { - auto proc_status_or = ParseProcStatus("Name:\tfoo\nName:\tfoo\n"); - EXPECT_THAT(proc_status_or, - PosixErrorIs(EINVAL, ::testing::StrEq("duplicate key \"Name\""))); -} - -TEST(ParseProcStatusTest, DetectsMissingTabs) { - EXPECT_THAT(ParseProcStatus("Name:foo\nPid: 1\n"), - IsPosixErrorOkAndHolds(UnorderedElementsAre(Pair("Name:foo", ""), - Pair("Pid: 1", "")))); -} - -TEST(ProcPidStatusTest, HasBasicFields) { - // Do this on a separate thread since we want tgid != tid. - ScopedThread([] { - const pid_t tgid = getpid(); - const pid_t tid = syscall(SYS_gettid); - EXPECT_NE(tgid, tid); - const auto thread_name = ASSERT_NO_ERRNO_AND_VALUE(ThreadName()); - - std::string status_str = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(absl::StrCat("/proc/", tid, "/status"))); - - ASSERT_FALSE(status_str.empty()); - const auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(status_str)); - EXPECT_THAT(status, IsSupersetOf({Pair("Name", thread_name), - Pair("Tgid", absl::StrCat(tgid)), - Pair("Pid", absl::StrCat(tid)), - Pair("PPid", absl::StrCat(getppid()))})); - }); -} - -TEST(ProcPidStatusTest, StateRunning) { - // Task must be running when reading the file. - const pid_t tid = syscall(SYS_gettid); - std::string status_str = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(absl::StrCat("/proc/", tid, "/status"))); - - EXPECT_THAT(ParseProcStatus(status_str), - IsPosixErrorOkAndHolds(Contains(Pair("State", "R (running)")))); -} - -TEST(ProcPidStatusTest, StateSleeping_NoRandomSave) { - // Starts a child process that blocks and checks that State is sleeping. - auto res = WithSubprocess( - [&](int pid) -> PosixError { - // Because this test is timing based we will disable cooperative saving - // and the test itself also has random saving disabled. - const DisableSave ds; - // Try multiple times in case the child isn't sleeping when status file - // is read. - MonotonicTimer timer; - timer.Start(); - for (;;) { - ASSIGN_OR_RETURN_ERRNO( - std::string status_str, - GetContents(absl::StrCat("/proc/", pid, "/status"))); - ASSIGN_OR_RETURN_ERRNO(auto map, ParseProcStatus(status_str)); - if (map["State"] == std::string("S (sleeping)")) { - // Test passed! - return NoError(); - } - if (timer.Duration() > absl::Seconds(10)) { - return PosixError(ETIMEDOUT, "Timeout waiting for child to sleep"); - } - absl::SleepFor(absl::Milliseconds(10)); - } - }, - nullptr, nullptr); - ASSERT_NO_ERRNO(res); -} - -TEST(ProcPidStatusTest, ValuesAreTabDelimited) { - std::string status_str = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/status")); - ASSERT_FALSE(status_str.empty()); - for (absl::string_view const line : - absl::StrSplit(status_str, '\n', absl::SkipWhitespace())) { - EXPECT_NE(std::string::npos, line.find(":\t")); - } -} - -// Threads properly counts running threads. -// -// TODO(mpratt): Test zombied threads while the thread group leader is still -// running with generalized fork and clone children from the wait test. -TEST(ProcPidStatusTest, Threads) { - char buf[4096] = {}; - EXPECT_THAT(ReadWhileRunning("status", buf, sizeof(buf) - 1), - SyscallSucceedsWithValue(Gt(0))); - - auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(buf)); - auto it = status.find("Threads"); - ASSERT_NE(it, status.end()); - int threads = -1; - EXPECT_TRUE(absl::SimpleAtoi(it->second, &threads)) - << "Threads value " << it->second << " is not a number"; - // Don't make assumptions about the exact number of threads, as it may not be - // constant. - EXPECT_GE(threads, 1); - - memset(buf, 0, sizeof(buf)); - EXPECT_THAT(ReadWhileZombied("status", buf, sizeof(buf) - 1), - SyscallSucceedsWithValue(Gt(0))); - - status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(buf)); - it = status.find("Threads"); - ASSERT_NE(it, status.end()); - threads = -1; - EXPECT_TRUE(absl::SimpleAtoi(it->second, &threads)) - << "Threads value " << it->second << " is not a number"; - // There must be only the thread group leader remaining, zombied. - EXPECT_EQ(threads, 1); -} - -// Returns true if all characters in s are digits. -bool IsDigits(absl::string_view s) { - return std::all_of(s.begin(), s.end(), absl::ascii_isdigit); -} - -TEST(ProcPidStatTest, VmStats) { - std::string status_str = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/status")); - ASSERT_FALSE(status_str.empty()); - auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(status_str)); - - const auto vss_it = status.find("VmSize"); - ASSERT_NE(vss_it, status.end()); - - absl::string_view vss_str(vss_it->second); - - // Room for the " kB" suffix plus at least one digit. - ASSERT_GT(vss_str.length(), 3); - EXPECT_TRUE(absl::EndsWith(vss_str, " kB")); - // Everything else is part of a number. - EXPECT_TRUE(IsDigits(vss_str.substr(0, vss_str.length() - 3))) << vss_str; - // ... which is not 0. - EXPECT_NE('0', vss_str[0]); - - const auto rss_it = status.find("VmRSS"); - ASSERT_NE(rss_it, status.end()); - - absl::string_view rss_str(rss_it->second); - - // Room for the " kB" suffix plus at least one digit. - ASSERT_GT(rss_str.length(), 3); - EXPECT_TRUE(absl::EndsWith(rss_str, " kB")); - // Everything else is part of a number. - EXPECT_TRUE(IsDigits(rss_str.substr(0, rss_str.length() - 3))) << rss_str; - // ... which is not 0. - EXPECT_NE('0', rss_str[0]); - - const auto data_it = status.find("VmData"); - ASSERT_NE(data_it, status.end()); - - absl::string_view data_str(data_it->second); - - // Room for the " kB" suffix plus at least one digit. - ASSERT_GT(data_str.length(), 3); - EXPECT_TRUE(absl::EndsWith(data_str, " kB")); - // Everything else is part of a number. - EXPECT_TRUE(IsDigits(data_str.substr(0, data_str.length() - 3))) << data_str; - // ... which is not 0. - EXPECT_NE('0', data_str[0]); -} - -// Parse an array of NUL-terminated char* arrays, returning a vector of -// strings. -std::vector<std::string> ParseNulTerminatedStrings(std::string contents) { - EXPECT_EQ('\0', contents.back()); - // The split will leave an empty string if the NUL-byte remains, so pop - // it. - contents.pop_back(); - - return absl::StrSplit(contents, '\0'); -} - -TEST(ProcPidCmdline, MatchesArgv) { - std::vector<std::string> proc_cmdline = ParseNulTerminatedStrings( - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/cmdline"))); - EXPECT_THAT(saved_argv, ContainerEq(proc_cmdline)); -} - -TEST(ProcPidEnviron, MatchesEnviron) { - std::vector<std::string> proc_environ = ParseNulTerminatedStrings( - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/environ"))); - // Get the environment from the environ variable, which we will compare with - // /proc/self/environ. - std::vector<std::string> env; - for (char** v = environ; *v; v++) { - env.push_back(*v); - } - EXPECT_THAT(env, ContainerEq(proc_environ)); -} - -TEST(ProcPidCmdline, SubprocessForkSameCmdline) { - std::vector<std::string> proc_cmdline_parent; - std::vector<std::string> proc_cmdline; - proc_cmdline_parent = ParseNulTerminatedStrings( - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/cmdline"))); - auto res = WithSubprocess( - [&](int pid) -> PosixError { - ASSIGN_OR_RETURN_ERRNO( - auto raw_cmdline, - GetContents(absl::StrCat("/proc/", pid, "/cmdline"))); - proc_cmdline = ParseNulTerminatedStrings(raw_cmdline); - return NoError(); - }, - nullptr, nullptr); - ASSERT_NO_ERRNO(res); - - for (size_t i = 0; i < proc_cmdline_parent.size(); i++) { - EXPECT_EQ(proc_cmdline_parent[i], proc_cmdline[i]); - } -} - -TEST(ProcPidCmdline, SubprocessSeekCmdline) { - FileDescriptor fd; - ASSERT_NO_ERRNO(WithSubprocess( - [&](int pid) -> PosixError { - // Running. Open /proc/pid/cmdline. - ASSIGN_OR_RETURN_ERRNO( - fd, Open(absl::StrCat("/proc/", pid, "/cmdline"), O_RDONLY)); - return NoError(); - }, - [&](int pid) -> PosixError { - // Zombie, but seek should still succeed. - int ret = lseek(fd.get(), 0x801, 0); - if (ret < 0) { - return PosixError(errno); - } - return NoError(); - }, - [&](int pid) -> PosixError { - // Exited. - int ret = lseek(fd.get(), 0x801, 0); - if (ret < 0) { - return PosixError(errno); - } - return NoError(); - })); -} - -// Test whether /proc/PID/ symlinks can be read for a running process. -TEST(ProcPidSymlink, SubprocessRunning) { - char buf[1]; - - EXPECT_THAT(ReadlinkWhileRunning("exe", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadlinkWhileRunning("ns/net", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadlinkWhileRunning("ns/pid", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadlinkWhileRunning("ns/user", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST(ProcPidSymlink, SubprocessZombied) { - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - char buf[1]; - - int want = EACCES; - if (!IsRunningOnGvisor()) { - auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); - if (version.major > 4 || (version.major == 4 && version.minor > 3)) { - want = ENOENT; - } - } - - EXPECT_THAT(ReadlinkWhileZombied("exe", buf, sizeof(buf)), - SyscallFailsWithErrno(want)); - - if (!IsRunningOnGvisor()) { - EXPECT_THAT(ReadlinkWhileZombied("ns/net", buf, sizeof(buf)), - SyscallFailsWithErrno(want)); - } - - // FIXME(gvisor.dev/issue/164): Inconsistent behavior between linux on proc - // files. - // - // ~4.3: Syscall fails with EACCES. - // 4.17: Syscall succeeds and returns 1. - // - if (!IsRunningOnGvisor()) { - return; - } - - EXPECT_THAT(ReadlinkWhileZombied("ns/pid", buf, sizeof(buf)), - SyscallFailsWithErrno(want)); - - EXPECT_THAT(ReadlinkWhileZombied("ns/user", buf, sizeof(buf)), - SyscallFailsWithErrno(want)); -} - -// Test whether /proc/PID/ symlinks can be read for an exited process. -TEST(ProcPidSymlink, SubprocessExited) { - char buf[1]; - - EXPECT_THAT(ReadlinkWhileExited("exe", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - - EXPECT_THAT(ReadlinkWhileExited("ns/net", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - - EXPECT_THAT(ReadlinkWhileExited("ns/pid", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - - EXPECT_THAT(ReadlinkWhileExited("ns/user", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); -} - -// /proc/PID/exe points to the correct binary. -TEST(ProcPidExe, Subprocess) { - auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe")); - auto expected_absolute_path = - ASSERT_NO_ERRNO_AND_VALUE(MakeAbsolute(link, "")); - - char actual[PATH_MAX + 1] = {}; - ASSERT_THAT(ReadlinkWhileRunning("exe", actual, sizeof(actual)), - SyscallSucceedsWithValue(Gt(0))); - EXPECT_EQ(actual, expected_absolute_path); -} - -// /proc/PID/cwd points to the correct directory. -TEST(ProcPidCwd, Subprocess) { - auto want = ASSERT_NO_ERRNO_AND_VALUE(GetCWD()); - - char got[PATH_MAX + 1] = {}; - ASSERT_THAT(ReadlinkWhileRunning("cwd", got, sizeof(got)), - SyscallSucceedsWithValue(Gt(0))); - EXPECT_EQ(got, want); -} - -// Test whether /proc/PID/ files can be read for a running process. -TEST(ProcPidFile, SubprocessRunning) { - char buf[1]; - - EXPECT_THAT(ReadWhileRunning("auxv", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("cmdline", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("comm", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("gid_map", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("io", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("maps", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("stat", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("status", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("uid_map", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("oom_score", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileRunning("oom_score_adj", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); -} - -// Test whether /proc/PID/ files can be read for a zombie process. -TEST(ProcPidFile, SubprocessZombie) { - char buf[1]; - - // FIXME(gvisor.dev/issue/164): Loosen requirement due to inconsistent - // behavior on different kernels. - // - // ~4.3: Succeds and returns 0. - // 4.17: Succeeds and returns 1. - // gVisor: Succeeds and returns 0. - EXPECT_THAT(ReadWhileZombied("auxv", buf, sizeof(buf)), SyscallSucceeds()); - - EXPECT_THAT(ReadWhileZombied("cmdline", buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(ReadWhileZombied("comm", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileZombied("gid_map", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileZombied("maps", buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); - - EXPECT_THAT(ReadWhileZombied("stat", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileZombied("status", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileZombied("uid_map", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileZombied("oom_score", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(ReadWhileZombied("oom_score_adj", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // FIXME(gvisor.dev/issue/164): Inconsistent behavior between gVisor and linux - // on proc files. - // - // ~4.3: Fails and returns EACCES. - // gVisor & 4.17: Succeeds and returns 1. - // - // EXPECT_THAT(ReadWhileZombied("io", buf, sizeof(buf)), - // SyscallFailsWithErrno(EACCES)); -} - -// Test whether /proc/PID/ files can be read for an exited process. -TEST(ProcPidFile, SubprocessExited) { - char buf[1]; - - // FIXME(gvisor.dev/issue/164): Inconsistent behavior between kernels. - // - // ~4.3: Fails and returns ESRCH. - // gVisor: Fails with ESRCH. - // 4.17: Succeeds and returns 1. - // - // EXPECT_THAT(ReadWhileExited("auxv", buf, sizeof(buf)), - // SyscallFailsWithErrno(ESRCH)); - - EXPECT_THAT(ReadWhileExited("cmdline", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - - if (!IsRunningOnGvisor()) { - // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. - EXPECT_THAT(ReadWhileExited("comm", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - } - - EXPECT_THAT(ReadWhileExited("gid_map", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - if (!IsRunningOnGvisor()) { - // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. - EXPECT_THAT(ReadWhileExited("io", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - } - - if (!IsRunningOnGvisor()) { - // FIXME(gvisor.dev/issue/164): Returns EOF on gVisor. - EXPECT_THAT(ReadWhileExited("maps", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - } - - if (!IsRunningOnGvisor()) { - // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. - EXPECT_THAT(ReadWhileExited("stat", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - } - - if (!IsRunningOnGvisor()) { - // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. - EXPECT_THAT(ReadWhileExited("status", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - } - - EXPECT_THAT(ReadWhileExited("uid_map", buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - if (!IsRunningOnGvisor()) { - // FIXME(gvisor.dev/issue/164): Succeeds on gVisor. - EXPECT_THAT(ReadWhileExited("oom_score", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); - } - - EXPECT_THAT(ReadWhileExited("oom_score_adj", buf, sizeof(buf)), - SyscallFailsWithErrno(ESRCH)); -} - -PosixError DirContains(absl::string_view path, - const std::vector<std::string>& expect, - const std::vector<std::string>& exclude) { - ASSIGN_OR_RETURN_ERRNO(auto listing, ListDir(path, false)); - - for (auto& expected_entry : expect) { - auto cursor = std::find(listing.begin(), listing.end(), expected_entry); - if (cursor == listing.end()) { - return PosixError( - ENOENT, - absl::StrCat("Failed to find one or more paths in '", path, "'")); - } - } - for (auto& excluded_entry : exclude) { - auto cursor = std::find(listing.begin(), listing.end(), excluded_entry); - if (cursor != listing.end()) { - return PosixError(ENOENT, absl::StrCat("File '", excluded_entry, - "' found in path '", path, "'")); - } - } - return NoError(); -} - -PosixError EventuallyDirContains(absl::string_view path, - const std::vector<std::string>& expect, - const std::vector<std::string>& exclude) { - constexpr int kRetryCount = 100; - const absl::Duration kRetryDelay = absl::Milliseconds(100); - - for (int i = 0; i < kRetryCount; ++i) { - auto res = DirContains(path, expect, exclude); - if (res.ok()) { - return res; - } else if (i < kRetryCount - 1) { - // Sleep if this isn't the final iteration. - absl::SleepFor(kRetryDelay); - } - } - return PosixError(ETIMEDOUT, - "Timed out while waiting for directory to contain files "); -} - -std::vector<std::string> TaskFiles(const std::vector<pid_t>& pids) { - return ApplyVec<std::string>([](const pid_t p) { return absl::StrCat(p); }, - pids); -} - -TEST(ProcTask, Basic) { - EXPECT_NO_ERRNO( - DirContains("/proc/self/task", {".", "..", absl::StrCat(getpid())}, {})); -} - -// Helper class for creating a new task in the current thread group. -class BlockingChild { - public: - BlockingChild() : thread_([=] { Start(); }) {} - ~BlockingChild() { Join(); } - - pid_t Tid() const { - absl::MutexLock ml(&mu_); - mu_.Await(absl::Condition(&tid_ready_)); - return tid_; - } - - void Join() { - { - absl::MutexLock ml(&mu_); - stop_ = true; - } - thread_.Join(); - } - - private: - void Start() { - absl::MutexLock ml(&mu_); - tid_ = syscall(__NR_gettid); - tid_ready_ = true; - mu_.Await(absl::Condition(&stop_)); - } - - mutable absl::Mutex mu_; - bool stop_ ABSL_GUARDED_BY(mu_) = false; - pid_t tid_; - bool tid_ready_ ABSL_GUARDED_BY(mu_) = false; - - // Must be last to ensure that the destructor for the thread is run before - // any other member of the object is destroyed. - ScopedThread thread_; -}; - -TEST(ProcTask, NewThreadAppears) { - BlockingChild child1; - EXPECT_NO_ERRNO( - DirContains("/proc/self/task", TaskFiles({child1.Tid()}), {})); -} - -TEST(ProcTask, KilledThreadsDisappear) { - BlockingChild child1; - EXPECT_NO_ERRNO( - DirContains("/proc/self/task", TaskFiles({child1.Tid()}), {})); - - // Stat child1's task file. Regression test for b/32097707. - struct stat statbuf; - const std::string child1_task_file = - absl::StrCat("/proc/self/task/", child1.Tid()); - EXPECT_THAT(stat(child1_task_file.c_str(), &statbuf), SyscallSucceeds()); - - BlockingChild child2; - EXPECT_NO_ERRNO(DirContains("/proc/self/task", - TaskFiles({child1.Tid(), child2.Tid()}), {})); - - BlockingChild child3; - BlockingChild child4; - BlockingChild child5; - EXPECT_NO_ERRNO( - DirContains("/proc/self/task", - TaskFiles({child1.Tid(), child2.Tid(), child3.Tid(), - child4.Tid(), child5.Tid()}), - {})); - - child2.Join(); - EXPECT_NO_ERRNO(EventuallyDirContains( - "/proc/self/task", - TaskFiles({child1.Tid(), child3.Tid(), child4.Tid(), child5.Tid()}), - TaskFiles({child2.Tid()}))); - - child1.Join(); - child4.Join(); - EXPECT_NO_ERRNO(EventuallyDirContains( - "/proc/self/task", TaskFiles({child3.Tid(), child5.Tid()}), - TaskFiles({child2.Tid(), child1.Tid(), child4.Tid()}))); - - // Stat child1's task file again. This time it should fail. See b/32097707. - EXPECT_THAT(stat(child1_task_file.c_str(), &statbuf), - SyscallFailsWithErrno(ENOENT)); - - child3.Join(); - child5.Join(); - EXPECT_NO_ERRNO( - EventuallyDirContains("/proc/self/task", {}, - TaskFiles({child2.Tid(), child1.Tid(), child4.Tid(), - child3.Tid(), child5.Tid()}))); -} - -TEST(ProcTask, ChildTaskDir) { - BlockingChild child1; - EXPECT_NO_ERRNO( - DirContains("/proc/self/task", TaskFiles({child1.Tid()}), {})); - EXPECT_NO_ERRNO(DirContains(absl::StrCat("/proc/", child1.Tid(), "/task"), - TaskFiles({child1.Tid()}), {})); -} - -PosixError VerifyPidDir(std::string path) { - return DirContains(path, {"exe", "fd", "io", "maps", "ns", "stat", "status"}, - {}); -} - -TEST(ProcTask, VerifyTaskDir) { - EXPECT_NO_ERRNO(VerifyPidDir("/proc/self")); - - EXPECT_NO_ERRNO(VerifyPidDir(absl::StrCat("/proc/self/task/", getpid()))); - BlockingChild child1; - EXPECT_NO_ERRNO(VerifyPidDir(absl::StrCat("/proc/self/task/", child1.Tid()))); - - // Only the first level of task directories should contain the 'task' - // directory. That is: - // - // /proc/1234/task <- should exist - // /proc/1234/task/1234/task <- should not exist - // /proc/1234/task/1235/task <- should not exist (where 1235 is in the same - // thread group as 1234). - EXPECT_NO_ERRNO( - DirContains(absl::StrCat("/proc/self/task/", getpid()), {}, {"task"})); -} - -TEST(ProcTask, TaskDirCannotBeDeleted) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - EXPECT_THAT(rmdir("/proc/self/task"), SyscallFails()); - EXPECT_THAT(rmdir(absl::StrCat("/proc/self/task/", getpid()).c_str()), - SyscallFailsWithErrno(EACCES)); -} - -TEST(ProcTask, TaskDirHasCorrectMetadata) { - struct stat st; - EXPECT_THAT(stat("/proc/self/task", &st), SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - - // Verify file is readable and executable by everyone. - mode_t expected_permissions = - S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - mode_t permissions = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); - EXPECT_EQ(expected_permissions, permissions); -} - -TEST(ProcTask, TaskDirCanSeekToEnd) { - const FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/task", O_RDONLY)); - EXPECT_THAT(lseek(dirfd.get(), 0, SEEK_END), SyscallSucceeds()); -} - -TEST(ProcTask, VerifyTaskDirNlinks) { - const auto fn = [] { - // A task directory will have 3 links if the taskgroup has a single - // thread. For example, the following shows where the links to - // '/proc/12345/task' comes from for a single threaded process with pid - // 12345: - // - // /proc/12345/task <-- 1 link for the directory itself - // . <-- link from "." - // .. - // 12345 - // . - // .. <-- link from ".." to parent. - // <other contents of a task dir> - // - // We can't assert an absolute number of links since we don't control how - // many threads the test framework spawns. Instead, we'll ensure creating a - // new thread increases the number of links as expected. - - // Once we reach the test body, we can count on the thread count being - // stable unless we spawn a new one. - const uint64_t initial_links = - TEST_CHECK_NO_ERRNO_AND_VALUE(Links("/proc/self/task")); - TEST_CHECK(initial_links >= 3); - - // For each new subtask, we should gain a new link. - BlockingChild child1; - uint64_t links = TEST_CHECK_NO_ERRNO_AND_VALUE(Links("/proc/self/task")); - TEST_CHECK(links == initial_links + 1); - - BlockingChild child2; - links = TEST_CHECK_NO_ERRNO_AND_VALUE(Links("/proc/self/task")); - TEST_CHECK(links == initial_links + 2); - }; - // Run as a forked process to prevent terminating tasks from other tests to - // show up here and race with the count. - EXPECT_THAT(InForkedProcess(fn), IsPosixErrorOkAndHolds(0)); -} - -TEST(ProcTask, CommContainsThreadNameAndTrailingNewline) { - constexpr char kThreadName[] = "TestThread12345"; - ASSERT_THAT(prctl(PR_SET_NAME, kThreadName), SyscallSucceeds()); - - auto thread_name = ASSERT_NO_ERRNO_AND_VALUE( - GetContents(JoinPath("/proc", absl::StrCat(getpid()), "task", - absl::StrCat(syscall(SYS_gettid)), "comm"))); - EXPECT_EQ(absl::StrCat(kThreadName, "\n"), thread_name); -} - -TEST(ProcTaskNs, NsDirExistsAndHasCorrectMetadata) { - EXPECT_NO_ERRNO(DirContains("/proc/self/ns", {"net", "pid", "user"}, {})); - - // Let's just test the 'pid' entry, all of them are very similar. - struct stat st; - EXPECT_THAT(lstat("/proc/self/ns/pid", &st), SyscallSucceeds()); - EXPECT_TRUE(S_ISLNK(st.st_mode)); - - auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/ns/pid")); - EXPECT_THAT(link, ::testing::StartsWith("pid:[")); -} - -TEST(ProcTaskNs, AccessOnNsNodeSucceeds) { - EXPECT_THAT(access("/proc/self/ns/pid", F_OK), SyscallSucceeds()); -} - -TEST(ProcSysKernelHostname, Exists) { - EXPECT_THAT(open("/proc/sys/kernel/hostname", O_RDONLY), SyscallSucceeds()); -} - -TEST(ProcSysKernelHostname, MatchesUname) { - struct utsname buf; - EXPECT_THAT(uname(&buf), SyscallSucceeds()); - const std::string hostname = absl::StrCat(buf.nodename, "\n"); - auto procfs_hostname = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/hostname")); - EXPECT_EQ(procfs_hostname, hostname); -} - -TEST(ProcSysVmMmapMinAddr, HasNumericValue) { - const std::string mmap_min_addr_str = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/vm/mmap_min_addr")); - uintptr_t mmap_min_addr; - EXPECT_TRUE(absl::SimpleAtoi(mmap_min_addr_str, &mmap_min_addr)) - << "/proc/sys/vm/mmap_min_addr does not contain a numeric value: " - << mmap_min_addr_str; -} - -TEST(ProcSysVmOvercommitMemory, HasNumericValue) { - const std::string overcommit_memory_str = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/vm/overcommit_memory")); - uintptr_t overcommit_memory; - EXPECT_TRUE(absl::SimpleAtoi(overcommit_memory_str, &overcommit_memory)) - << "/proc/sys/vm/overcommit_memory does not contain a numeric value: " - << overcommit_memory; -} - -// Check that link for proc fd entries point the target node, not the -// symlink itself. Regression test for b/31155070. -TEST(ProcTaskFd, FstatatFollowsSymlink) { - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - struct stat sproc = {}; - EXPECT_THAT( - fstatat(-1, absl::StrCat("/proc/self/fd/", fd.get()).c_str(), &sproc, 0), - SyscallSucceeds()); - - struct stat sfile = {}; - EXPECT_THAT(fstatat(-1, file.path().c_str(), &sfile, 0), SyscallSucceeds()); - - // If fstatat follows the fd symlink, the device and inode numbers should - // match at a minimum. - EXPECT_EQ(sproc.st_dev, sfile.st_dev); - EXPECT_EQ(sproc.st_ino, sfile.st_ino); - EXPECT_EQ(0, memcmp(&sfile, &sproc, sizeof(sfile))); -} - -TEST(ProcFilesystems, Bug65172365) { - std::string proc_filesystems = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/filesystems")); - ASSERT_FALSE(proc_filesystems.empty()); -} - -TEST(ProcFilesystems, PresenceOfShmMaxMniAll) { - uint64_t shmmax = 0; - uint64_t shmall = 0; - uint64_t shmmni = 0; - std::string proc_file; - proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmax")); - ASSERT_FALSE(proc_file.empty()); - ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmax)); - proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmall")); - ASSERT_FALSE(proc_file.empty()); - ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmall)); - proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/shmmni")); - ASSERT_FALSE(proc_file.empty()); - ASSERT_TRUE(absl::SimpleAtoi(proc_file, &shmmni)); - - ASSERT_GT(shmmax, 0); - ASSERT_GT(shmall, 0); - ASSERT_GT(shmmni, 0); - ASSERT_LE(shmall, shmmax); - - // These values should never be higher than this by default, for more - // information see uapi/linux/shm.h - ASSERT_LE(shmmax, ULONG_MAX - (1UL << 24)); - ASSERT_LE(shmall, ULONG_MAX - (1UL << 24)); -} - -TEST(ProcFilesystems, PresenceOfSem) { - uint32_t semmsl = 0; - uint32_t semmns = 0; - uint32_t semopm = 0; - uint32_t semmni = 0; - std::string proc_file; - proc_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/sys/kernel/sem")); - ASSERT_FALSE(proc_file.empty()); - std::vector<absl::string_view> sem_limits = - absl::StrSplit(proc_file, absl::ByAnyChar("\t"), absl::SkipWhitespace()); - ASSERT_EQ(sem_limits.size(), 4); - ASSERT_TRUE(absl::SimpleAtoi(sem_limits[0], &semmsl)); - ASSERT_TRUE(absl::SimpleAtoi(sem_limits[1], &semmns)); - ASSERT_TRUE(absl::SimpleAtoi(sem_limits[2], &semopm)); - ASSERT_TRUE(absl::SimpleAtoi(sem_limits[3], &semmni)); - - ASSERT_EQ(semmsl, SEMMSL); - ASSERT_EQ(semmns, SEMMNS); - ASSERT_EQ(semopm, SEMOPM); - ASSERT_EQ(semmni, SEMMNI); -} - -// Check that /proc/mounts is a symlink to self/mounts. -TEST(ProcMounts, IsSymlink) { - auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/mounts")); - EXPECT_EQ(link, "self/mounts"); -} - -TEST(ProcSelfMountinfo, RequiredFieldsArePresent) { - auto mountinfo = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/mountinfo")); - EXPECT_THAT( - mountinfo, - AllOf( - // Root mount. - ContainsRegex( - R"([0-9]+ [0-9]+ [0-9]+:[0-9]+ /\S* / (rw|ro).*- \S+ \S+ (rw|ro)\S*)"), - // Proc mount - always rw. - ContainsRegex( - R"([0-9]+ [0-9]+ [0-9]+:[0-9]+ / /proc rw.*- \S+ \S+ rw\S*)"))); -} - -// Check that /proc/self/mounts looks something like a real mounts file. -TEST(ProcSelfMounts, RequiredFieldsArePresent) { - auto mounts = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/mounts")); - EXPECT_THAT(mounts, - AllOf( - // Root mount. - ContainsRegex(R"(\S+ / \S+ (rw|ro)\S* [0-9]+ [0-9]+\s)"), - // Root mount. - ContainsRegex(R"(\S+ /proc \S+ rw\S* [0-9]+ [0-9]+\s)"))); -} - -void CheckDuplicatesRecursively(std::string path) { - std::vector<std::string> child_dirs; - - // There is the known issue of the linux procfs, that two consequent calls of - // readdir can return the same entry twice if between these calls one or more - // entries have been removed from this directory. - int max_attempts = 5; - for (int i = 0; i < max_attempts; i++) { - child_dirs.clear(); - errno = 0; - bool success = true; - DIR* dir = opendir(path.c_str()); - if (dir == nullptr) { - // Ignore any directories we can't read or missing directories as the - // directory could have been deleted/mutated from the time the parent - // directory contents were read. - return; - } - auto dir_closer = Cleanup([&dir]() { closedir(dir); }); - absl::node_hash_set<std::string> children; - while (true) { - // Readdir(3): If the end of the directory stream is reached, NULL is - // returned and errno is not changed. If an error occurs, NULL is - // returned and errno is set appropriately. To distinguish end of stream - // and from an error, set errno to zero before calling readdir() and then - // check the value of errno if NULL is returned. - errno = 0; - struct dirent* dp = readdir(dir); - if (dp == nullptr) { - // Linux will return EINVAL when calling getdents on a /proc/tid/net - // file corresponding to a zombie task. - // See fs/proc/proc_net.c:proc_tgid_net_readdir(). - // - // We just ignore the directory in this case. - if (errno == EINVAL && absl::StartsWith(path, "/proc/") && - absl::EndsWith(path, "/net")) { - break; - } - // We may also see permission failures traversing some files. - if (errno == EACCES && absl::StartsWith(path, "/proc/")) { - break; - } - - // Otherwise, no errors are allowed. - ASSERT_EQ(errno, 0) << path; - break; // We're done. - } - - const std::string name = dp->d_name; - - if (name == "." || name == "..") { - continue; - } - - // Ignore a duplicate entry if it isn't the last attempt. - if (i == max_attempts - 1) { - ASSERT_EQ(children.find(name), children.end()) - << absl::StrCat(path, "/", name); - } else if (children.find(name) != children.end()) { - std::cerr << "Duplicate entry: " << i << ":" - << absl::StrCat(path, "/", name) << std::endl; - success = false; - break; - } - children.insert(name); - - if (dp->d_type == DT_DIR) { - child_dirs.push_back(name); - } - } - if (success) { - break; - } - } - for (auto dname = child_dirs.begin(); dname != child_dirs.end(); dname++) { - CheckDuplicatesRecursively(absl::StrCat(path, "/", *dname)); - } -} - -TEST(Proc, NoDuplicates) { CheckDuplicatesRecursively("/proc"); } - -// Most /proc/PID files are owned by the task user with SUID_DUMP_USER. -TEST(ProcPid, UserDumpableOwner) { - int before; - ASSERT_THAT(before = prctl(PR_GET_DUMPABLE), SyscallSucceeds()); - auto cleanup = Cleanup([before] { - ASSERT_THAT(prctl(PR_SET_DUMPABLE, before), SyscallSucceeds()); - }); - - EXPECT_THAT(prctl(PR_SET_DUMPABLE, SUID_DUMP_USER), SyscallSucceeds()); - - // This applies to the task directory itself and files inside. - struct stat st; - ASSERT_THAT(stat("/proc/self/", &st), SyscallSucceeds()); - EXPECT_EQ(st.st_uid, geteuid()); - EXPECT_EQ(st.st_gid, getegid()); - - ASSERT_THAT(stat("/proc/self/stat", &st), SyscallSucceeds()); - EXPECT_EQ(st.st_uid, geteuid()); - EXPECT_EQ(st.st_gid, getegid()); -} - -// /proc/PID files are owned by root with SUID_DUMP_DISABLE. -TEST(ProcPid, RootDumpableOwner) { - int before; - ASSERT_THAT(before = prctl(PR_GET_DUMPABLE), SyscallSucceeds()); - auto cleanup = Cleanup([before] { - ASSERT_THAT(prctl(PR_SET_DUMPABLE, before), SyscallSucceeds()); - }); - - EXPECT_THAT(prctl(PR_SET_DUMPABLE, SUID_DUMP_DISABLE), SyscallSucceeds()); - - // This *does not* applies to the task directory itself (or other 0555 - // directories), but does to files inside. - struct stat st; - ASSERT_THAT(stat("/proc/self/", &st), SyscallSucceeds()); - EXPECT_EQ(st.st_uid, geteuid()); - EXPECT_EQ(st.st_gid, getegid()); - - // This file is owned by root. Also allow nobody in case this test is running - // in a userns without root mapped. - ASSERT_THAT(stat("/proc/self/stat", &st), SyscallSucceeds()); - EXPECT_THAT(st.st_uid, AnyOf(Eq(0), Eq(65534))); - EXPECT_THAT(st.st_gid, AnyOf(Eq(0), Eq(65534))); -} - -TEST(Proc, GetdentsEnoent) { - FileDescriptor fd; - ASSERT_NO_ERRNO(WithSubprocess( - [&](int pid) -> PosixError { - // Running. - ASSIGN_OR_RETURN_ERRNO(fd, Open(absl::StrCat("/proc/", pid, "/task"), - O_RDONLY | O_DIRECTORY)); - - return NoError(); - }, - nullptr, nullptr)); - char buf[1024]; - ASSERT_THAT(syscall(SYS_getdents64, fd.get(), buf, sizeof(buf)), - SyscallFailsWithErrno(ENOENT)); -} - -void CheckSyscwFromIOFile(const std::string& path, const std::string& regex) { - std::string output; - ASSERT_NO_ERRNO(GetContents(path, &output)); - ASSERT_THAT(output, ContainsRegex(absl::StrCat("syscw:\\s+", regex, "\n"))); -} - -// Checks that there is variable accounting of IO between threads/tasks. -TEST(Proc, PidTidIOAccounting) { - absl::Notification notification; - - // Run a thread with a bunch of writes. Check that io account records exactly - // the number of write calls. File open/close is there to prevent buffering. - ScopedThread writer([¬ification] { - const int num_writes = 100; - for (int i = 0; i < num_writes; i++) { - auto path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - ASSERT_NO_ERRNO(SetContents(path.path(), "a")); - } - notification.Notify(); - const std::string& writer_dir = - absl::StrCat("/proc/", getpid(), "/task/", gettid(), "/io"); - - CheckSyscwFromIOFile(writer_dir, std::to_string(num_writes)); - }); - - // Run a thread and do no writes. Check that no writes are recorded. - ScopedThread noop([¬ification] { - notification.WaitForNotification(); - const std::string& noop_dir = - absl::StrCat("/proc/", getpid(), "/task/", gettid(), "/io"); - - CheckSyscwFromIOFile(noop_dir, "0"); - }); - - writer.Join(); - noop.Join(); -} - -TEST(Proc, Statfs) { - struct statfs st; - EXPECT_THAT(statfs("/proc", &st), SyscallSucceeds()); - if (IsRunningWithVFS1()) { - EXPECT_EQ(st.f_type, ANON_INODE_FS_MAGIC); - } else { - EXPECT_EQ(st.f_type, PROC_SUPER_MAGIC); - } - EXPECT_EQ(st.f_bsize, getpagesize()); - EXPECT_EQ(st.f_namelen, NAME_MAX); -} - -} // namespace -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - for (int i = 0; i < argc; ++i) { - gvisor::testing::saved_argv.emplace_back(std::string(argv[i])); - } - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc deleted file mode 100644 index 20f1dc305..000000000 --- a/test/syscalls/linux/proc_net.cc +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <errno.h> -#include <netinet/in.h> -#include <poll.h> -#include <sys/socket.h> -#include <sys/syscall.h> -#include <sys/types.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -constexpr const char kProcNet[] = "/proc/net"; -constexpr const char kIpForward[] = "/proc/sys/net/ipv4/ip_forward"; -constexpr const char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range"; - -TEST(ProcNetSymlinkTarget, FileMode) { - struct stat s; - ASSERT_THAT(stat(kProcNet, &s), SyscallSucceeds()); - EXPECT_EQ(s.st_mode & S_IFMT, S_IFDIR); - EXPECT_EQ(s.st_mode & 0777, 0555); -} - -TEST(ProcNetSymlink, FileMode) { - struct stat s; - ASSERT_THAT(lstat(kProcNet, &s), SyscallSucceeds()); - EXPECT_EQ(s.st_mode & S_IFMT, S_IFLNK); - EXPECT_EQ(s.st_mode & 0777, 0777); -} - -TEST(ProcNetSymlink, Contents) { - char buf[40] = {}; - int n = readlink(kProcNet, buf, sizeof(buf)); - ASSERT_THAT(n, SyscallSucceeds()); - - buf[n] = 0; - EXPECT_STREQ(buf, "self/net"); -} - -TEST(ProcNetIfInet6, Format) { - auto ifinet6 = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/if_inet6")); - EXPECT_THAT(ifinet6, - ::testing::MatchesRegex( - // Ex: "00000000000000000000000000000001 01 80 10 80 lo\n" - "^([a-f0-9]{32}( [a-f0-9]{2}){4} +[a-z][a-z0-9]*\n)+$")); -} - -TEST(ProcSysNetIpv4Sack, Exists) { - EXPECT_THAT(open("/proc/sys/net/ipv4/tcp_sack", O_RDONLY), SyscallSucceeds()); -} - -TEST(ProcSysNetIpv4Sack, CanReadAndWrite) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - auto const fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/net/ipv4/tcp_sack", O_RDWR)); - - char buf; - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected tcp_sack: " << buf; - - char to_write = (buf == '1') ? '0' : '1'; - EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0), - SyscallSucceedsWithValue(sizeof(to_write))); - - buf = 0; - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - EXPECT_EQ(buf, to_write); -} - -// DeviceEntry is an entry in /proc/net/dev -struct DeviceEntry { - std::string name; - uint64_t stats[16]; -}; - -PosixErrorOr<std::vector<DeviceEntry>> GetDeviceMetricsFromProc( - const std::string dev) { - std::vector<std::string> lines = absl::StrSplit(dev, '\n'); - std::vector<DeviceEntry> entries; - - // /proc/net/dev prints 2 lines of headers followed by a line of metrics for - // each network interface. - for (unsigned i = 2; i < lines.size(); i++) { - // Ignore empty lines. - if (lines[i].empty()) { - continue; - } - - std::vector<std::string> values = - absl::StrSplit(lines[i], ' ', absl::SkipWhitespace()); - - // Interface name + 16 values. - if (values.size() != 17) { - return PosixError(EINVAL, "invalid line: " + lines[i]); - } - - DeviceEntry entry; - entry.name = values[0]; - // Skip the interface name and read only the values. - for (unsigned j = 1; j < 17; j++) { - uint64_t num; - if (!absl::SimpleAtoi(values[j], &num)) { - return PosixError(EINVAL, "invalid value: " + values[j]); - } - entry.stats[j - 1] = num; - } - - entries.push_back(entry); - } - - return entries; -} - -// TEST(ProcNetDev, Format) tests that /proc/net/dev is parsable and -// contains at least one entry. -TEST(ProcNetDev, Format) { - auto dev = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/dev")); - auto entries = ASSERT_NO_ERRNO_AND_VALUE(GetDeviceMetricsFromProc(dev)); - - EXPECT_GT(entries.size(), 0); -} - -PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp, - 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. - // Only search the headers. - for (unsigned i = 0; i < snmp_vec.size(); i = i + 2) { - if (!absl::StartsWith(snmp_vec[i], type)) continue; - - std::vector<std::string> fields = - absl::StrSplit(snmp_vec[i], ' ', absl::SkipWhitespace()); - - EXPECT_TRUE((i + 1) < snmp_vec.size()); - std::vector<std::string> values = - absl::StrSplit(snmp_vec[i + 1], ' ', absl::SkipWhitespace()); - - EXPECT_TRUE(!fields.empty() && fields.size() == values.size()); - - // Metrics start at the first index. - for (unsigned j = 1; j < fields.size(); j++) { - if (fields[j] == item) { - uint64_t val; - if (!absl::SimpleAtoi(values[j], &val)) { - return PosixError(EINVAL, - absl::StrCat("field is not a number: ", values[j])); - } - - return val; - } - } - } - // We should never get here. - return PosixError( - EINVAL, absl::StrCat("failed to find ", type, "/", item, " in:", snmp)); -} - -TEST(ProcNetSnmp, TcpReset_NoRandomSave) { - // TODO(gvisor.dev/issue/866): epsocket metrics are not savable. - DisableSave ds; - - uint64_t oldAttemptFails; - uint64_t oldActiveOpens; - uint64_t oldOutRsts; - auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - oldActiveOpens = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens")); - oldOutRsts = - ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Tcp", "OutRsts")); - oldAttemptFails = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails")); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0)); - - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_port = htons(1234), - }; - - ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1); - ASSERT_THAT(connect(s.get(), (struct sockaddr*)&sin, sizeof(sin)), - SyscallFailsWithErrno(ECONNREFUSED)); - - uint64_t newAttemptFails; - uint64_t newActiveOpens; - uint64_t newOutRsts; - snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - newActiveOpens = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens")); - newOutRsts = - ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Tcp", "OutRsts")); - newAttemptFails = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails")); - - EXPECT_EQ(oldActiveOpens, newActiveOpens - 1); - EXPECT_EQ(oldOutRsts, newOutRsts - 1); - EXPECT_EQ(oldAttemptFails, newAttemptFails - 1); -} - -TEST(ProcNetSnmp, TcpEstab_NoRandomSave) { - // TODO(gvisor.dev/issue/866): epsocket metrics are not savable. - DisableSave ds; - - uint64_t oldEstabResets; - uint64_t oldActiveOpens; - uint64_t oldPassiveOpens; - uint64_t oldCurrEstab; - auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - oldActiveOpens = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens")); - oldPassiveOpens = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "PassiveOpens")); - oldCurrEstab = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab")); - oldEstabResets = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "EstabResets")); - - FileDescriptor s_listen = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0)); - struct sockaddr_in sin = { - .sin_family = AF_INET, - .sin_port = 0, - }; - - 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)), - 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), - 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)), - SyscallSucceeds()); - - auto s_accept = - ASSERT_NO_ERRNO_AND_VALUE(Accept(s_listen.get(), nullptr, nullptr)); - - uint64_t newEstabResets; - uint64_t newActiveOpens; - uint64_t newPassiveOpens; - uint64_t newCurrEstab; - snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - newActiveOpens = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "ActiveOpens")); - newPassiveOpens = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "PassiveOpens")); - newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab")); - - EXPECT_EQ(oldActiveOpens, newActiveOpens - 1); - EXPECT_EQ(oldPassiveOpens, newPassiveOpens - 1); - EXPECT_EQ(oldCurrEstab, newCurrEstab - 2); - - // Send 1 byte from client to server. - ASSERT_THAT(send(s_connect.get(), "a", 1, 0), SyscallSucceedsWithValue(1)); - - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - - // Wait until server-side fd sees the data on its side but don't read it. - struct pollfd poll_fd = {s_accept.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now close server-side fd without reading the data which leads to a RST - // packet sent to client side. - s_accept.reset(-1); - - // Wait until client-side fd sees RST packet. - struct pollfd poll_fd1 = {s_connect.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd1, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now close client-side fd. - s_connect.reset(-1); - - // Wait until the process of the netstack. - absl::SleepFor(absl::Seconds(1)); - - snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab")); - newEstabResets = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Tcp", "EstabResets")); - - EXPECT_EQ(oldCurrEstab, newCurrEstab); - EXPECT_EQ(oldEstabResets, newEstabResets - 2); -} - -TEST(ProcNetSnmp, UdpNoPorts_NoRandomSave) { - // TODO(gvisor.dev/issue/866): epsocket metrics are not savable. - DisableSave ds; - - uint64_t oldOutDatagrams; - uint64_t oldNoPorts; - auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - oldOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams")); - oldNoPorts = - ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts")); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - struct sockaddr_in sin = { - .sin_family = AF_INET, - .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)), - SyscallSucceedsWithValue(1)); - - uint64_t newOutDatagrams; - uint64_t newNoPorts; - snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - newOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams")); - newNoPorts = - ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts")); - - EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1); - EXPECT_EQ(oldNoPorts, newNoPorts - 1); -} - -TEST(ProcNetSnmp, UdpIn_NoRandomSave) { - // TODO(gvisor.dev/issue/866): epsocket metrics are not savable. - const DisableSave ds; - - uint64_t oldOutDatagrams; - uint64_t oldInDatagrams; - auto snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - oldOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams")); - oldInDatagrams = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams")); - - std::cerr << "snmp: " << std::endl << snmp << std::endl; - FileDescriptor server = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - struct sockaddr_in sin = { - .sin_family = AF_INET, - .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)), - SyscallSucceeds()); - // Get the port bound by the server socket. - socklen_t addrlen = sizeof(sin); - ASSERT_THAT( - 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)), - SyscallSucceedsWithValue(1)); - - char buf[128]; - ASSERT_THAT(recvfrom(server.get(), buf, sizeof(buf), 0, NULL, NULL), - SyscallSucceedsWithValue(1)); - - uint64_t newOutDatagrams; - uint64_t newInDatagrams; - snmp = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - std::cerr << "new snmp: " << std::endl << snmp << std::endl; - newOutDatagrams = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Udp", "OutDatagrams")); - newInDatagrams = ASSERT_NO_ERRNO_AND_VALUE( - GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams")); - - EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1); - EXPECT_EQ(oldInDatagrams, newInDatagrams - 1); -} - -TEST(ProcNetSnmp, CheckNetStat) { - // TODO(b/155123175): SNMP and netstat don't work on gVisor. - SKIP_IF(IsRunningOnGvisor()); - - std::string contents = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/netstat")); - - int name_count = 0; - int value_count = 0; - std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n'); - for (long unsigned int i = 0; i + 1 < lines.size(); i += 2) { - std::vector<absl::string_view> names = - absl::StrSplit(lines[i], absl::ByAnyChar("\t ")); - std::vector<absl::string_view> values = - absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t ")); - EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i] - << "' and '" << lines[i + 1] << "'"; - for (long unsigned int j = 0; j < names.size() && j < values.size(); ++j) { - if (names[j] == "TCPOrigDataSent" || names[j] == "TCPSynRetrans" || - names[j] == "TCPDSACKRecv" || names[j] == "TCPDSACKOfoRecv") { - ++name_count; - int64_t val; - if (absl::SimpleAtoi(values[j], &val)) { - ++value_count; - } - } - } - } - EXPECT_EQ(name_count, 4); - EXPECT_EQ(value_count, 4); -} - -TEST(ProcNetSnmp, Stat) { - struct stat st = {}; - ASSERT_THAT(stat("/proc/net/snmp", &st), SyscallSucceeds()); -} - -TEST(ProcNetSnmp, CheckSnmp) { - // TODO(b/155123175): SNMP and netstat don't work on gVisor. - SKIP_IF(IsRunningOnGvisor()); - - std::string contents = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/snmp")); - - int name_count = 0; - int value_count = 0; - std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n'); - for (long unsigned int i = 0; i + 1 < lines.size(); i += 2) { - std::vector<absl::string_view> names = - absl::StrSplit(lines[i], absl::ByAnyChar("\t ")); - std::vector<absl::string_view> values = - absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t ")); - EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i] - << "' and '" << lines[i + 1] << "'"; - for (long unsigned int j = 0; j < names.size() && j < values.size(); ++j) { - if (names[j] == "RetransSegs") { - ++name_count; - int64_t val; - if (absl::SimpleAtoi(values[j], &val)) { - ++value_count; - } - } - } - } - EXPECT_EQ(name_count, 1); - EXPECT_EQ(value_count, 1); -} - -TEST(ProcSysNetIpv4Recovery, Exists) { - EXPECT_THAT(open("/proc/sys/net/ipv4/tcp_recovery", O_RDONLY), - SyscallSucceeds()); -} - -TEST(ProcSysNetIpv4Recovery, CanReadAndWrite) { - // TODO(b/162988252): Enable save/restore for this test after the bug is - // fixed. - DisableSave ds; - - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - auto const fd = ASSERT_NO_ERRNO_AND_VALUE( - Open("/proc/sys/net/ipv4/tcp_recovery", O_RDWR)); - - char buf[10] = {'\0'}; - char to_write = '2'; - - // Check initial value is set to 1. - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(to_write) + 1)); - if (IsRunningOnGvisor()) { - // TODO(gvisor.dev/issue/5243): TCPRACKLossDetection = 1 should be turned on - // by default. - EXPECT_EQ(strcmp(buf, "0\n"), 0); - } else { - EXPECT_EQ(strcmp(buf, "1\n"), 0); - } - - // Set tcp_recovery to one of the allowed constants. - EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0), - SyscallSucceedsWithValue(sizeof(to_write))); - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(to_write) + 1)); - EXPECT_EQ(strcmp(buf, "2\n"), 0); - - // Set tcp_recovery to any random value. - char kMessage[] = "100"; - EXPECT_THAT(PwriteFd(fd.get(), kMessage, strlen(kMessage), 0), - SyscallSucceedsWithValue(strlen(kMessage))); - EXPECT_THAT(PreadFd(fd.get(), buf, sizeof(kMessage), 0), - SyscallSucceedsWithValue(sizeof(kMessage))); - EXPECT_EQ(strcmp(buf, "100\n"), 0); -} - -TEST(ProcSysNetIpv4IpForward, Exists) { - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY)); -} - -TEST(ProcSysNetIpv4IpForward, DefaultValueEqZero) { - // Test is only valid in sandbox. Not hermetic in native tests - // running on a arbitrary machine. - SKIP_IF(!IsRunningOnGvisor()); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY)); - - char buf = 101; - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_EQ(buf, '0') << "unexpected ip_forward: " << buf; -} - -TEST(ProcSysNetIpv4IpForward, CanReadAndWrite) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE)))); - - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDWR)); - - char buf; - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected ip_forward: " << buf; - - // constexpr char to_write = '1'; - char to_write = (buf == '1') ? '0' : '1'; - EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0), - SyscallSucceedsWithValue(sizeof(to_write))); - - buf = 0; - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - EXPECT_EQ(buf, to_write); -} - -TEST(ProcSysNetPortRange, CanReadAndWrite) { - int min; - int max; - std::string rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile)); - ASSERT_EQ(rangefile.back(), '\n'); - rangefile.pop_back(); - std::vector<std::string> range = - absl::StrSplit(rangefile, absl::ByAnyChar("\t ")); - ASSERT_GT(range.size(), 1); - ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min)); - ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max)); - EXPECT_LE(min, max); - - // If the file isn't writable, there's nothing else to do here. - if (access(kRangeFile, W_OK)) { - return; - } - - constexpr int kSize = 77; - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(kRangeFile, O_WRONLY | O_TRUNC, 0)); - max = min + kSize; - const std::string small_range = absl::StrFormat("%d %d", min, max); - ASSERT_THAT(write(fd.get(), small_range.c_str(), small_range.size()), - SyscallSucceedsWithValue(small_range.size())); - - rangefile = ASSERT_NO_ERRNO_AND_VALUE(GetContents(kRangeFile)); - ASSERT_EQ(rangefile.back(), '\n'); - rangefile.pop_back(); - range = absl::StrSplit(rangefile, absl::ByAnyChar("\t ")); - ASSERT_GT(range.size(), 1); - ASSERT_TRUE(absl::SimpleAtoi(range.front(), &min)); - ASSERT_TRUE(absl::SimpleAtoi(range.back(), &max)); - EXPECT_EQ(min + kSize, max); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/proc_net_tcp.cc b/test/syscalls/linux/proc_net_tcp.cc deleted file mode 100644 index 5b6e3e3cd..000000000 --- a/test/syscalls/linux/proc_net_tcp.cc +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright 2019 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 <netinet/tcp.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_split.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using absl::StrCat; -using absl::StrSplit; - -constexpr char kProcNetTCPHeader[] = - " sl local_address rem_address st tx_queue rx_queue tr tm->when " - "retrnsmt uid timeout inode " - " "; - -// TCPEntry represents a single entry from /proc/net/tcp. -struct TCPEntry { - uint32_t local_addr; - uint16_t local_port; - - uint32_t remote_addr; - uint16_t remote_port; - - uint64_t state; - uint64_t uid; - uint64_t inode; -}; - -// Finds the first entry in 'entries' for which 'predicate' returns true. -// Returns true on match, and sets 'match' to a copy of the matching entry. If -// 'match' is null, it's ignored. -bool FindBy(const std::vector<TCPEntry>& entries, TCPEntry* match, - std::function<bool(const TCPEntry&)> predicate) { - for (const TCPEntry& entry : entries) { - if (predicate(entry)) { - if (match != nullptr) { - *match = entry; - } - return true; - } - } - return false; -} - -bool FindByLocalAddr(const std::vector<TCPEntry>& entries, TCPEntry* match, - const struct sockaddr* addr) { - uint32_t host = IPFromInetSockaddr(addr); - uint16_t port = PortFromInetSockaddr(addr); - return FindBy(entries, match, [host, port](const TCPEntry& e) { - return (e.local_addr == host && e.local_port == port); - }); -} - -bool FindByRemoteAddr(const std::vector<TCPEntry>& entries, TCPEntry* match, - const struct sockaddr* addr) { - uint32_t host = IPFromInetSockaddr(addr); - uint16_t port = PortFromInetSockaddr(addr); - return FindBy(entries, match, [host, port](const TCPEntry& e) { - return (e.remote_addr == host && e.remote_port == port); - }); -} - -// Returns a parsed representation of /proc/net/tcp entries. -PosixErrorOr<std::vector<TCPEntry>> ProcNetTCPEntries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/net/tcp", &content)); - - bool found_header = false; - std::vector<TCPEntry> entries; - std::vector<std::string> lines = StrSplit(content, '\n'); - std::cerr << "<contents of /proc/net/tcp>" << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - - if (!found_header) { - EXPECT_EQ(line, kProcNetTCPHeader); - found_header = true; - continue; - } - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/net/tcp. - // - // Example entries: - // - // clang-format off - // - // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode - // 0: 00000000:006F 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 1968 1 0000000000000000 100 0 0 10 0 - // 1: 0100007F:7533 00000000:0000 0A 00000000:00000000 00:00000000 00000000 120 0 10684 1 0000000000000000 100 0 0 10 0 - // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - // - // clang-format on - - TCPEntry entry; - std::vector<std::string> fields = - StrSplit(line, absl::ByAnyChar(": "), absl::SkipEmpty()); - - ASSIGN_OR_RETURN_ERRNO(entry.local_addr, AtoiBase(fields[1], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.local_port, AtoiBase(fields[2], 16)); - - ASSIGN_OR_RETURN_ERRNO(entry.remote_addr, AtoiBase(fields[3], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.remote_port, AtoiBase(fields[4], 16)); - - ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.uid, Atoi<uint64_t>(fields[11])); - ASSIGN_OR_RETURN_ERRNO(entry.inode, Atoi<uint64_t>(fields[13])); - - entries.push_back(entry); - } - std::cerr << "<end of /proc/net/tcp>" << std::endl; - - return entries; -} - -TEST(ProcNetTCP, Exists) { - const std::string content = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/tcp")); - const std::string header_line = StrCat(kProcNetTCPHeader, "\n"); - if (IsRunningOnGvisor()) { - // Should be just the header since we don't have any tcp sockets yet. - EXPECT_EQ(content, header_line); - } else { - // On a general linux machine, we could have abitrary sockets on the system, - // so just check the header. - EXPECT_THAT(content, ::testing::StartsWith(header_line)); - } -} - -TEST(ProcNetTCP, EntryUID) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPAcceptBindSocketPair(0).Create()); - std::vector<TCPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); - TCPEntry e; - ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->first_addr())); - EXPECT_EQ(e.uid, geteuid()); - ASSERT_TRUE(FindByRemoteAddr(entries, &e, sockets->first_addr())); - EXPECT_EQ(e.uid, geteuid()); -} - -TEST(ProcNetTCP, BindAcceptConnect) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPAcceptBindSocketPair(0).Create()); - std::vector<TCPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); - // We can only make assertions about the total number of entries if we control - // the entire "machine". - if (IsRunningOnGvisor()) { - EXPECT_EQ(entries.size(), 2); - } - - EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->first_addr())); - EXPECT_TRUE(FindByRemoteAddr(entries, nullptr, sockets->first_addr())); -} - -TEST(ProcNetTCP, InodeReasonable) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPAcceptBindSocketPair(0).Create()); - std::vector<TCPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); - - TCPEntry accepted_entry; - ASSERT_TRUE(FindByLocalAddr(entries, &accepted_entry, sockets->first_addr())); - EXPECT_NE(accepted_entry.inode, 0); - - TCPEntry client_entry; - ASSERT_TRUE(FindByRemoteAddr(entries, &client_entry, sockets->first_addr())); - EXPECT_NE(client_entry.inode, 0); - EXPECT_NE(accepted_entry.inode, client_entry.inode); -} - -TEST(ProcNetTCP, State) { - std::unique_ptr<FileDescriptor> server = - ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPUnboundSocket(0).Create()); - - auto test_addr = V4Loopback(); - ASSERT_THAT( - bind(server->get(), reinterpret_cast<struct sockaddr*>(&test_addr.addr), - test_addr.addr_len), - SyscallSucceeds()); - - struct sockaddr addr; - socklen_t addrlen = sizeof(struct sockaddr); - ASSERT_THAT(getsockname(server->get(), &addr, &addrlen), SyscallSucceeds()); - ASSERT_EQ(addrlen, sizeof(struct sockaddr)); - - ASSERT_THAT(listen(server->get(), 10), SyscallSucceeds()); - std::vector<TCPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); - TCPEntry listen_entry; - ASSERT_TRUE(FindByLocalAddr(entries, &listen_entry, &addr)); - EXPECT_EQ(listen_entry.state, TCP_LISTEN); - - std::unique_ptr<FileDescriptor> client = - ASSERT_NO_ERRNO_AND_VALUE(IPv4TCPUnboundSocket(0).Create()); - ASSERT_THAT(RetryEINTR(connect)(client->get(), &addr, addrlen), - SyscallSucceeds()); - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); - ASSERT_TRUE(FindByLocalAddr(entries, &listen_entry, &addr)); - EXPECT_EQ(listen_entry.state, TCP_LISTEN); - TCPEntry client_entry; - ASSERT_TRUE(FindByRemoteAddr(entries, &client_entry, &addr)); - EXPECT_EQ(client_entry.state, TCP_ESTABLISHED); - - FileDescriptor accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(server->get(), nullptr, nullptr)); - - const uint32_t accepted_local_host = IPFromInetSockaddr(&addr); - const uint16_t accepted_local_port = PortFromInetSockaddr(&addr); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCPEntries()); - TCPEntry accepted_entry; - ASSERT_TRUE(FindBy(entries, &accepted_entry, - [client_entry, accepted_local_host, - accepted_local_port](const TCPEntry& e) { - return e.local_addr == accepted_local_host && - e.local_port == accepted_local_port && - e.remote_addr == client_entry.local_addr && - e.remote_port == client_entry.local_port; - })); - EXPECT_EQ(accepted_entry.state, TCP_ESTABLISHED); -} - -constexpr char kProcNetTCP6Header[] = - " sl local_address remote_address" - " st tx_queue rx_queue tr tm->when retrnsmt" - " uid timeout inode"; - -// TCP6Entry represents a single entry from /proc/net/tcp6. -struct TCP6Entry { - struct in6_addr local_addr; - uint16_t local_port; - - struct in6_addr remote_addr; - uint16_t remote_port; - - uint64_t state; - uint64_t uid; - uint64_t inode; -}; - -bool IPv6AddrEqual(const struct in6_addr* a1, const struct in6_addr* a2) { - return memcmp(a1, a2, sizeof(struct in6_addr)) == 0; -} - -// Finds the first entry in 'entries' for which 'predicate' returns true. -// Returns true on match, and sets 'match' to a copy of the matching entry. If -// 'match' is null, it's ignored. -bool FindBy6(const std::vector<TCP6Entry>& entries, TCP6Entry* match, - std::function<bool(const TCP6Entry&)> predicate) { - for (const TCP6Entry& entry : entries) { - if (predicate(entry)) { - if (match != nullptr) { - *match = entry; - } - return true; - } - } - return false; -} - -const struct in6_addr* IP6FromInetSockaddr(const struct sockaddr* addr) { - auto* addr6 = reinterpret_cast<const struct sockaddr_in6*>(addr); - return &addr6->sin6_addr; -} - -bool FindByLocalAddr6(const std::vector<TCP6Entry>& entries, TCP6Entry* match, - const struct sockaddr* addr) { - const struct in6_addr* local = IP6FromInetSockaddr(addr); - uint16_t port = PortFromInetSockaddr(addr); - return FindBy6(entries, match, [local, port](const TCP6Entry& e) { - return (IPv6AddrEqual(&e.local_addr, local) && e.local_port == port); - }); -} - -bool FindByRemoteAddr6(const std::vector<TCP6Entry>& entries, TCP6Entry* match, - const struct sockaddr* addr) { - const struct in6_addr* remote = IP6FromInetSockaddr(addr); - uint16_t port = PortFromInetSockaddr(addr); - return FindBy6(entries, match, [remote, port](const TCP6Entry& e) { - return (IPv6AddrEqual(&e.remote_addr, remote) && e.remote_port == port); - }); -} - -void ReadIPv6Address(std::string s, struct in6_addr* addr) { - uint32_t a0, a1, a2, a3; - const char* fmt = "%08X%08X%08X%08X"; - EXPECT_EQ(sscanf(s.c_str(), fmt, &a0, &a1, &a2, &a3), 4); - - uint8_t* b = addr->s6_addr; - *((uint32_t*)&b[0]) = a0; - *((uint32_t*)&b[4]) = a1; - *((uint32_t*)&b[8]) = a2; - *((uint32_t*)&b[12]) = a3; -} - -// Returns a parsed representation of /proc/net/tcp6 entries. -PosixErrorOr<std::vector<TCP6Entry>> ProcNetTCP6Entries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/net/tcp6", &content)); - - bool found_header = false; - std::vector<TCP6Entry> entries; - std::vector<std::string> lines = StrSplit(content, '\n'); - std::cerr << "<contents of /proc/net/tcp6>" << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - - if (!found_header) { - EXPECT_EQ(line, kProcNetTCP6Header); - found_header = true; - continue; - } - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/net/tcp6. - // - // Example entries: - // - // clang-format off - // - // sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode - // 0: 00000000000000000000000000000000:1F90 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 876340 1 ffff8803da9c9380 100 0 0 10 0 - // 1: 00000000000000000000000000000000:C350 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 876987 1 ffff8803ec408000 100 0 0 10 0 - // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 - // - // clang-format on - - TCP6Entry entry; - std::vector<std::string> fields = - StrSplit(line, absl::ByAnyChar(": "), absl::SkipEmpty()); - - ReadIPv6Address(fields[1], &entry.local_addr); - ASSIGN_OR_RETURN_ERRNO(entry.local_port, AtoiBase(fields[2], 16)); - ReadIPv6Address(fields[3], &entry.remote_addr); - ASSIGN_OR_RETURN_ERRNO(entry.remote_port, AtoiBase(fields[4], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.uid, Atoi<uint64_t>(fields[11])); - ASSIGN_OR_RETURN_ERRNO(entry.inode, Atoi<uint64_t>(fields[13])); - - entries.push_back(entry); - } - std::cerr << "<end of /proc/net/tcp6>" << std::endl; - - return entries; -} - -TEST(ProcNetTCP6, Exists) { - const std::string content = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/tcp6")); - const std::string header_line = StrCat(kProcNetTCP6Header, "\n"); - if (IsRunningOnGvisor()) { - // Should be just the header since we don't have any tcp sockets yet. - EXPECT_EQ(content, header_line); - } else { - // On a general linux machine, we could have abitrary sockets on the system, - // so just check the header. - EXPECT_THAT(content, ::testing::StartsWith(header_line)); - } -} - -TEST(ProcNetTCP6, EntryUID) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPAcceptBindSocketPair(0).Create()); - std::vector<TCP6Entry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); - TCP6Entry e; - - ASSERT_TRUE(FindByLocalAddr6(entries, &e, sockets->first_addr())); - EXPECT_EQ(e.uid, geteuid()); - ASSERT_TRUE(FindByRemoteAddr6(entries, &e, sockets->first_addr())); - EXPECT_EQ(e.uid, geteuid()); -} - -TEST(ProcNetTCP6, BindAcceptConnect) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPAcceptBindSocketPair(0).Create()); - std::vector<TCP6Entry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); - // We can only make assertions about the total number of entries if we control - // the entire "machine". - if (IsRunningOnGvisor()) { - EXPECT_EQ(entries.size(), 2); - } - - EXPECT_TRUE(FindByLocalAddr6(entries, nullptr, sockets->first_addr())); - EXPECT_TRUE(FindByRemoteAddr6(entries, nullptr, sockets->first_addr())); -} - -TEST(ProcNetTCP6, InodeReasonable) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPAcceptBindSocketPair(0).Create()); - std::vector<TCP6Entry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); - - TCP6Entry accepted_entry; - - ASSERT_TRUE( - FindByLocalAddr6(entries, &accepted_entry, sockets->first_addr())); - EXPECT_NE(accepted_entry.inode, 0); - - TCP6Entry client_entry; - ASSERT_TRUE(FindByRemoteAddr6(entries, &client_entry, sockets->first_addr())); - EXPECT_NE(client_entry.inode, 0); - EXPECT_NE(accepted_entry.inode, client_entry.inode); -} - -TEST(ProcNetTCP6, State) { - std::unique_ptr<FileDescriptor> server = - ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPUnboundSocket(0).Create()); - - auto test_addr = V6Loopback(); - ASSERT_THAT( - bind(server->get(), reinterpret_cast<struct sockaddr*>(&test_addr.addr), - test_addr.addr_len), - SyscallSucceeds()); - - struct sockaddr_in6 addr6; - socklen_t addrlen = sizeof(struct sockaddr_in6); - auto* addr = reinterpret_cast<struct sockaddr*>(&addr6); - ASSERT_THAT(getsockname(server->get(), addr, &addrlen), SyscallSucceeds()); - ASSERT_EQ(addrlen, sizeof(struct sockaddr_in6)); - - ASSERT_THAT(listen(server->get(), 10), SyscallSucceeds()); - std::vector<TCP6Entry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); - TCP6Entry listen_entry; - - ASSERT_TRUE(FindByLocalAddr6(entries, &listen_entry, addr)); - EXPECT_EQ(listen_entry.state, TCP_LISTEN); - - std::unique_ptr<FileDescriptor> client = - ASSERT_NO_ERRNO_AND_VALUE(IPv6TCPUnboundSocket(0).Create()); - ASSERT_THAT(RetryEINTR(connect)(client->get(), addr, addrlen), - SyscallSucceeds()); - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); - ASSERT_TRUE(FindByLocalAddr6(entries, &listen_entry, addr)); - EXPECT_EQ(listen_entry.state, TCP_LISTEN); - TCP6Entry client_entry; - ASSERT_TRUE(FindByRemoteAddr6(entries, &client_entry, addr)); - EXPECT_EQ(client_entry.state, TCP_ESTABLISHED); - - FileDescriptor accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(server->get(), nullptr, nullptr)); - - const struct in6_addr* local = IP6FromInetSockaddr(addr); - const uint16_t accepted_local_port = PortFromInetSockaddr(addr); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetTCP6Entries()); - TCP6Entry accepted_entry; - ASSERT_TRUE(FindBy6( - entries, &accepted_entry, - [client_entry, local, accepted_local_port](const TCP6Entry& e) { - return IPv6AddrEqual(&e.local_addr, local) && - e.local_port == accepted_local_port && - IPv6AddrEqual(&e.remote_addr, &client_entry.local_addr) && - e.remote_port == client_entry.local_port; - })); - EXPECT_EQ(accepted_entry.state, TCP_ESTABLISHED); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/proc_net_udp.cc b/test/syscalls/linux/proc_net_udp.cc deleted file mode 100644 index 786b4b4af..000000000 --- a/test/syscalls/linux/proc_net_udp.cc +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright 2019 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 <netinet/tcp.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_split.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using absl::StrCat; -using absl::StrFormat; -using absl::StrSplit; - -constexpr char kProcNetUDPHeader[] = - " sl local_address rem_address st tx_queue rx_queue tr tm->when " - "retrnsmt uid timeout inode ref pointer drops "; - -// UDPEntry represents a single entry from /proc/net/udp. -struct UDPEntry { - uint32_t local_addr; - uint16_t local_port; - - uint32_t remote_addr; - uint16_t remote_port; - - uint64_t state; - uint64_t uid; - uint64_t inode; -}; - -std::string DescribeFirstInetSocket(const SocketPair& sockets) { - const struct sockaddr* addr = sockets.first_addr(); - return StrFormat("First test socket: fd:%d %8X:%4X", sockets.first_fd(), - IPFromInetSockaddr(addr), PortFromInetSockaddr(addr)); -} - -std::string DescribeSecondInetSocket(const SocketPair& sockets) { - const struct sockaddr* addr = sockets.second_addr(); - return StrFormat("Second test socket fd:%d %8X:%4X", sockets.second_fd(), - IPFromInetSockaddr(addr), PortFromInetSockaddr(addr)); -} - -// Finds the first entry in 'entries' for which 'predicate' returns true. -// Returns true on match, and set 'match' to a copy of the matching entry. If -// 'match' is null, it's ignored. -bool FindBy(const std::vector<UDPEntry>& entries, UDPEntry* match, - std::function<bool(const UDPEntry&)> predicate) { - for (const UDPEntry& entry : entries) { - if (predicate(entry)) { - if (match != nullptr) { - *match = entry; - } - return true; - } - } - return false; -} - -bool FindByLocalAddr(const std::vector<UDPEntry>& entries, UDPEntry* match, - const struct sockaddr* addr) { - uint32_t host = IPFromInetSockaddr(addr); - uint16_t port = PortFromInetSockaddr(addr); - return FindBy(entries, match, [host, port](const UDPEntry& e) { - return (e.local_addr == host && e.local_port == port); - }); -} - -bool FindByRemoteAddr(const std::vector<UDPEntry>& entries, UDPEntry* match, - const struct sockaddr* addr) { - uint32_t host = IPFromInetSockaddr(addr); - uint16_t port = PortFromInetSockaddr(addr); - return FindBy(entries, match, [host, port](const UDPEntry& e) { - return (e.remote_addr == host && e.remote_port == port); - }); -} - -PosixErrorOr<uint64_t> InodeFromSocketFD(int fd) { - ASSIGN_OR_RETURN_ERRNO(struct stat s, Fstat(fd)); - if (!S_ISSOCK(s.st_mode)) { - return PosixError(EINVAL, StrFormat("FD %d is not a socket", fd)); - } - return s.st_ino; -} - -PosixErrorOr<bool> FindByFD(const std::vector<UDPEntry>& entries, - UDPEntry* match, int fd) { - ASSIGN_OR_RETURN_ERRNO(uint64_t inode, InodeFromSocketFD(fd)); - return FindBy(entries, match, - [inode](const UDPEntry& e) { return (e.inode == inode); }); -} - -// Returns a parsed representation of /proc/net/udp entries. -PosixErrorOr<std::vector<UDPEntry>> ProcNetUDPEntries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/net/udp", &content)); - - bool found_header = false; - std::vector<UDPEntry> entries; - std::vector<std::string> lines = StrSplit(content, '\n'); - std::cerr << "<contents of /proc/net/udp>" << std::endl; - for (const std::string& line : lines) { - std::cerr << line << std::endl; - - if (!found_header) { - EXPECT_EQ(line, kProcNetUDPHeader); - found_header = true; - continue; - } - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/net/udp. - // - // Example entries: - // - // clang-format off - // - // sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode ref pointer drops - // 3503: 0100007F:0035 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 33317 2 0000000000000000 0 - // 3518: 00000000:0044 00000000:0000 07 00000000:00000000 00:00000000 00000000 0 0 40394 2 0000000000000000 0 - // ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - // - // clang-format on - - UDPEntry entry; - std::vector<std::string> fields = - StrSplit(line, absl::ByAnyChar(": "), absl::SkipEmpty()); - - ASSIGN_OR_RETURN_ERRNO(entry.local_addr, AtoiBase(fields[1], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.local_port, AtoiBase(fields[2], 16)); - - ASSIGN_OR_RETURN_ERRNO(entry.remote_addr, AtoiBase(fields[3], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.remote_port, AtoiBase(fields[4], 16)); - - ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.uid, Atoi<uint64_t>(fields[11])); - ASSIGN_OR_RETURN_ERRNO(entry.inode, Atoi<uint64_t>(fields[13])); - - // Linux shares internal data structures between TCP and UDP sockets. The - // proc entries for UDP sockets share some fields with TCP sockets, but - // these fields should always be zero as they're not meaningful for UDP - // sockets. - EXPECT_EQ(fields[8], "00") << StrFormat("sl:%s, tr", fields[0]); - EXPECT_EQ(fields[9], "00000000") << StrFormat("sl:%s, tm->when", fields[0]); - EXPECT_EQ(fields[10], "00000000") - << StrFormat("sl:%s, retrnsmt", fields[0]); - EXPECT_EQ(fields[12], "0") << StrFormat("sl:%s, timeout", fields[0]); - - entries.push_back(entry); - } - std::cerr << "<end of /proc/net/udp>" << std::endl; - - return entries; -} - -TEST(ProcNetUDP, Exists) { - const std::string content = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/udp")); - const std::string header_line = StrCat(kProcNetUDPHeader, "\n"); - EXPECT_THAT(content, ::testing::StartsWith(header_line)); -} - -TEST(ProcNetUDP, EntryUID) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create()); - std::vector<UDPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - UDPEntry e; - ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->first_addr())) - << DescribeFirstInetSocket(*sockets); - EXPECT_EQ(e.uid, geteuid()); - ASSERT_TRUE(FindByRemoteAddr(entries, &e, sockets->first_addr())) - << DescribeSecondInetSocket(*sockets); - EXPECT_EQ(e.uid, geteuid()); -} - -TEST(ProcNetUDP, FindMutualEntries) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create()); - std::vector<UDPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - - EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->first_addr())) - << DescribeFirstInetSocket(*sockets); - EXPECT_TRUE(FindByRemoteAddr(entries, nullptr, sockets->first_addr())) - << DescribeSecondInetSocket(*sockets); - - EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->second_addr())) - << DescribeSecondInetSocket(*sockets); - EXPECT_TRUE(FindByRemoteAddr(entries, nullptr, sockets->second_addr())) - << DescribeFirstInetSocket(*sockets); -} - -TEST(ProcNetUDP, EntriesRemovedOnClose) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create()); - std::vector<UDPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - - EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->first_addr())) - << DescribeFirstInetSocket(*sockets); - EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->second_addr())) - << DescribeSecondInetSocket(*sockets); - - EXPECT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - // First socket's entry should be gone, but the second socket's entry should - // still exist. - EXPECT_FALSE(FindByLocalAddr(entries, nullptr, sockets->first_addr())) - << DescribeFirstInetSocket(*sockets); - EXPECT_TRUE(FindByLocalAddr(entries, nullptr, sockets->second_addr())) - << DescribeSecondInetSocket(*sockets); - - EXPECT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - // Both entries should be gone. - EXPECT_FALSE(FindByLocalAddr(entries, nullptr, sockets->first_addr())) - << DescribeFirstInetSocket(*sockets); - EXPECT_FALSE(FindByLocalAddr(entries, nullptr, sockets->second_addr())) - << DescribeSecondInetSocket(*sockets); -} - -PosixErrorOr<std::unique_ptr<FileDescriptor>> BoundUDPSocket() { - ASSIGN_OR_RETURN_ERRNO(std::unique_ptr<FileDescriptor> socket, - IPv4UDPUnboundSocket(0).Create()); - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = 0; - - int res = bind(socket->get(), reinterpret_cast<const struct sockaddr*>(&addr), - sizeof(addr)); - if (res) { - return PosixError(errno, "bind()"); - } - return socket; -} - -TEST(ProcNetUDP, BoundEntry) { - std::unique_ptr<FileDescriptor> socket = - ASSERT_NO_ERRNO_AND_VALUE(BoundUDPSocket()); - struct sockaddr addr; - socklen_t len = sizeof(addr); - ASSERT_THAT(getsockname(socket->get(), &addr, &len), SyscallSucceeds()); - uint16_t port = PortFromInetSockaddr(&addr); - - std::vector<UDPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - UDPEntry e; - ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(FindByFD(entries, &e, socket->get()))); - EXPECT_EQ(e.local_port, port); - EXPECT_EQ(e.remote_addr, 0); - EXPECT_EQ(e.remote_port, 0); -} - -TEST(ProcNetUDP, BoundSocketStateClosed) { - std::unique_ptr<FileDescriptor> socket = - ASSERT_NO_ERRNO_AND_VALUE(BoundUDPSocket()); - std::vector<UDPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - UDPEntry e; - ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(FindByFD(entries, &e, socket->get()))); - EXPECT_EQ(e.state, TCP_CLOSE); -} - -TEST(ProcNetUDP, ConnectedSocketStateEstablished) { - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(IPv4UDPBidirectionalBindSocketPair(0).Create()); - std::vector<UDPEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUDPEntries()); - - UDPEntry e; - ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->first_addr())) - << DescribeFirstInetSocket(*sockets); - EXPECT_EQ(e.state, TCP_ESTABLISHED); - - ASSERT_TRUE(FindByLocalAddr(entries, &e, sockets->second_addr())) - << DescribeSecondInetSocket(*sockets); - EXPECT_EQ(e.state, TCP_ESTABLISHED); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/proc_net_unix.cc b/test/syscalls/linux/proc_net_unix.cc deleted file mode 100644 index d61d94309..000000000 --- a/test/syscalls/linux/proc_net_unix.cc +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright 2019 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 "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_format.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_split.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using absl::StrCat; -using absl::StreamFormat; -using absl::StrFormat; - -constexpr char kProcNetUnixHeader[] = - "Num RefCount Protocol Flags Type St Inode Path"; - -// Possible values of the "st" field in a /proc/net/unix entry. Source: Linux -// kernel, include/uapi/linux/net.h. -enum { - SS_FREE = 0, // Not allocated - SS_UNCONNECTED, // Unconnected to any socket - SS_CONNECTING, // In process of connecting - SS_CONNECTED, // Connected to socket - SS_DISCONNECTING // In process of disconnecting -}; - -// UnixEntry represents a single entry from /proc/net/unix. -struct UnixEntry { - uintptr_t addr; - uint64_t refs; - uint64_t protocol; - uint64_t flags; - uint64_t type; - uint64_t state; - uint64_t inode; - std::string path; -}; - -// Abstract socket paths can have either trailing null bytes or '@'s as padding -// at the end, depending on the linux version. This function strips any such -// padding. -void StripAbstractPathPadding(std::string* s) { - const char pad_char = s->back(); - if (pad_char != '\0' && pad_char != '@') { - return; - } - - const auto last_pos = s->find_last_not_of(pad_char); - if (last_pos != std::string::npos) { - s->resize(last_pos + 1); - } -} - -// Precondition: addr must be a unix socket address (i.e. sockaddr_un) and -// addr->sun_path must be null-terminated. This is always the case if addr comes -// from Linux: -// -// Per man unix(7): -// -// "When the address of a pathname socket is returned (by [getsockname(2)]), its -// length is -// -// offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1 -// -// and sun_path contains the null-terminated pathname." -std::string ExtractPath(const struct sockaddr* addr) { - const char* path = - reinterpret_cast<const struct sockaddr_un*>(addr)->sun_path; - // Note: sockaddr_un.sun_path is an embedded character array of length - // UNIX_PATH_MAX, so we can always safely dereference the first 2 bytes below. - // - // We also rely on the path being null-terminated. - if (path[0] == 0) { - std::string abstract_path = StrCat("@", &path[1]); - StripAbstractPathPadding(&abstract_path); - return abstract_path; - } - return std::string(path); -} - -// Returns a parsed representation of /proc/net/unix entries. -PosixErrorOr<std::vector<UnixEntry>> ProcNetUnixEntries() { - std::string content; - RETURN_IF_ERRNO(GetContents("/proc/net/unix", &content)); - - bool skipped_header = false; - std::vector<UnixEntry> entries; - std::vector<std::string> lines = absl::StrSplit(content, '\n'); - std::cerr << "<contents of /proc/net/unix>" << std::endl; - for (const std::string& line : lines) { - // Emit the proc entry to the test output to provide context for the test - // results. - std::cerr << line << std::endl; - - if (!skipped_header) { - EXPECT_EQ(line, kProcNetUnixHeader); - skipped_header = true; - continue; - } - if (line.empty()) { - continue; - } - - // Parse a single entry from /proc/net/unix. - // - // Sample file: - // - // clang-format off - // - // Num RefCount Protocol Flags Type St Inode Path" - // ffffa130e7041c00: 00000002 00000000 00010000 0001 01 1299413685 /tmp/control_server/13293772586877554487 - // ffffa14f547dc400: 00000002 00000000 00010000 0001 01 3793 @remote_coredump - // - // clang-format on - // - // Note that from the second entry, the inode number can be padded using - // spaces, so we need to handle it separately during parsing. See - // net/unix/af_unix.c:unix_seq_show() for how these entries are produced. In - // particular, only the inode field is padded with spaces. - UnixEntry entry; - - // Process the first 6 fields, up to but not including "Inode". - std::vector<std::string> fields = - absl::StrSplit(line, absl::MaxSplits(' ', 6)); - - if (fields.size() < 7) { - return PosixError(EINVAL, StrFormat("Invalid entry: '%s'\n", line)); - } - - // AtoiBase can't handle the ':' in the "Num" field, so strip it out. - std::vector<std::string> addr = absl::StrSplit(fields[0], ':'); - ASSIGN_OR_RETURN_ERRNO(entry.addr, AtoiBase(addr[0], 16)); - - ASSIGN_OR_RETURN_ERRNO(entry.refs, AtoiBase(fields[1], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.protocol, AtoiBase(fields[2], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.flags, AtoiBase(fields[3], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.type, AtoiBase(fields[4], 16)); - ASSIGN_OR_RETURN_ERRNO(entry.state, AtoiBase(fields[5], 16)); - - absl::string_view rest = absl::StripAsciiWhitespace(fields[6]); - fields = absl::StrSplit(rest, absl::MaxSplits(' ', 1)); - if (fields.empty()) { - return PosixError( - EINVAL, StrFormat("Invalid entry, missing 'Inode': '%s'\n", line)); - } - ASSIGN_OR_RETURN_ERRNO(entry.inode, AtoiBase(fields[0], 10)); - - entry.path = ""; - if (fields.size() > 1) { - entry.path = fields[1]; - StripAbstractPathPadding(&entry.path); - } - - entries.push_back(entry); - } - std::cerr << "<end of /proc/net/unix>" << std::endl; - - return entries; -} - -// Finds the first entry in 'entries' for which 'predicate' returns true. -// Returns true on match, and sets 'match' to point to the matching entry. -bool FindBy(std::vector<UnixEntry> entries, UnixEntry* match, - std::function<bool(const UnixEntry&)> predicate) { - for (long unsigned int i = 0; i < entries.size(); ++i) { - if (predicate(entries[i])) { - *match = entries[i]; - return true; - } - } - return false; -} - -bool FindByPath(std::vector<UnixEntry> entries, UnixEntry* match, - const std::string& path) { - return FindBy(entries, match, - [path](const UnixEntry& e) { return e.path == path; }); -} - -TEST(ProcNetUnix, Exists) { - const std::string content = - ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/net/unix")); - const std::string header_line = StrCat(kProcNetUnixHeader, "\n"); - if (IsRunningOnGvisor()) { - // Should be just the header since we don't have any unix domain sockets - // yet. - EXPECT_EQ(content, header_line); - } else { - // However, on a general linux machine, we could have abitrary sockets on - // the system, so just check the header. - EXPECT_THAT(content, ::testing::StartsWith(header_line)); - } -} - -TEST(ProcNetUnix, FilesystemBindAcceptConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - FilesystemBoundUnixDomainSocketPair(SOCK_STREAM).Create()); - - std::string path1 = ExtractPath(sockets->first_addr()); - std::string path2 = ExtractPath(sockets->second_addr()); - std::cerr << StreamFormat("Server socket address (path1): %s\n", path1); - std::cerr << StreamFormat("Client socket address (path2): %s\n", path2); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - if (IsRunningOnGvisor()) { - EXPECT_EQ(entries.size(), 2); - } - - // The server-side socket's path is listed in the socket entry... - UnixEntry s1; - EXPECT_TRUE(FindByPath(entries, &s1, path1)); - - // ... but the client-side socket's path is not. - UnixEntry s2; - EXPECT_FALSE(FindByPath(entries, &s2, path2)); -} - -TEST(ProcNetUnix, AbstractBindAcceptConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - AbstractBoundUnixDomainSocketPair(SOCK_STREAM).Create()); - - std::string path1 = ExtractPath(sockets->first_addr()); - std::string path2 = ExtractPath(sockets->second_addr()); - std::cerr << StreamFormat("Server socket address (path1): '%s'\n", path1); - std::cerr << StreamFormat("Client socket address (path2): '%s'\n", path2); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - if (IsRunningOnGvisor()) { - EXPECT_EQ(entries.size(), 2); - } - - // The server-side socket's path is listed in the socket entry... - UnixEntry s1; - EXPECT_TRUE(FindByPath(entries, &s1, path1)); - - // ... but the client-side socket's path is not. - UnixEntry s2; - EXPECT_FALSE(FindByPath(entries, &s2, path2)); -} - -TEST(ProcNetUnix, SocketPair) { - // Under gvisor, ensure a socketpair() syscall creates exactly 2 new - // entries. We have no way to verify this under Linux, as we have no control - // over socket creation on a general Linux machine. - SKIP_IF(!IsRunningOnGvisor()); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - ASSERT_EQ(entries.size(), 0); - - auto sockets = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_STREAM).Create()); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - EXPECT_EQ(entries.size(), 2); -} - -TEST(ProcNetUnix, StreamSocketStateUnconnectedOnBind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - AbstractUnboundUnixDomainSocketPair(SOCK_STREAM).Create()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - - const std::string address = ExtractPath(sockets->first_addr()); - UnixEntry bind_entry; - ASSERT_TRUE(FindByPath(entries, &bind_entry, address)); - EXPECT_EQ(bind_entry.state, SS_UNCONNECTED); -} - -TEST(ProcNetUnix, StreamSocketStateStateUnconnectedOnListen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - AbstractUnboundUnixDomainSocketPair(SOCK_STREAM).Create()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - - const std::string address = ExtractPath(sockets->first_addr()); - UnixEntry bind_entry; - ASSERT_TRUE(FindByPath(entries, &bind_entry, address)); - EXPECT_EQ(bind_entry.state, SS_UNCONNECTED); - - ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - UnixEntry listen_entry; - ASSERT_TRUE( - FindByPath(entries, &listen_entry, ExtractPath(sockets->first_addr()))); - EXPECT_EQ(listen_entry.state, SS_UNCONNECTED); - // The bind and listen entries should refer to the same socket. - EXPECT_EQ(listen_entry.inode, bind_entry.inode); -} - -TEST(ProcNetUnix, StreamSocketStateStateConnectedOnAccept) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - AbstractUnboundUnixDomainSocketPair(SOCK_STREAM).Create()); - const std::string address = ExtractPath(sockets->first_addr()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds()); - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - UnixEntry listen_entry; - ASSERT_TRUE( - FindByPath(entries, &listen_entry, ExtractPath(sockets->first_addr()))); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - int clientfd; - ASSERT_THAT(clientfd = accept(sockets->first_fd(), nullptr, nullptr), - SyscallSucceeds()); - auto cleanup = Cleanup( - [clientfd]() { ASSERT_THAT(close(clientfd), SyscallSucceeds()); }); - - // Find the entry for the accepted socket. UDS proc entries don't have a - // remote address, so we distinguish the accepted socket from the listen - // socket by checking for a different inode. - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - UnixEntry accept_entry; - ASSERT_TRUE(FindBy( - entries, &accept_entry, [address, listen_entry](const UnixEntry& e) { - return e.path == address && e.inode != listen_entry.inode; - })); - EXPECT_EQ(accept_entry.state, SS_CONNECTED); - // Listen entry should still be in SS_UNCONNECTED state. - ASSERT_TRUE(FindBy(entries, &listen_entry, - [&sockets, listen_entry](const UnixEntry& e) { - return e.path == ExtractPath(sockets->first_addr()) && - e.inode == listen_entry.inode; - })); - EXPECT_EQ(listen_entry.state, SS_UNCONNECTED); -} - -TEST(ProcNetUnix, DgramSocketStateDisconnectingOnBind) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - AbstractUnboundUnixDomainSocketPair(SOCK_DGRAM).Create()); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - - // On gVisor, the only two UDS on the system are the ones we just created and - // we rely on this to locate the test socket entries in the remainder of the - // test. On a generic Linux system, we have no easy way to locate the - // corresponding entries, as they don't have an address yet. - if (IsRunningOnGvisor()) { - ASSERT_EQ(entries.size(), 2); - for (const auto& e : entries) { - ASSERT_EQ(e.state, SS_DISCONNECTING); - } - } - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - const std::string address = ExtractPath(sockets->first_addr()); - UnixEntry bind_entry; - ASSERT_TRUE(FindByPath(entries, &bind_entry, address)); - EXPECT_EQ(bind_entry.state, SS_UNCONNECTED); -} - -TEST(ProcNetUnix, DgramSocketStateConnectingOnConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE( - AbstractUnboundUnixDomainSocketPair(SOCK_DGRAM).Create()); - - std::vector<UnixEntry> entries = - ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - - // On gVisor, the only two UDS on the system are the ones we just created and - // we rely on this to locate the test socket entries in the remainder of the - // test. On a generic Linux system, we have no easy way to locate the - // corresponding entries, as they don't have an address yet. - if (IsRunningOnGvisor()) { - ASSERT_EQ(entries.size(), 2); - for (const auto& e : entries) { - ASSERT_EQ(e.state, SS_DISCONNECTING); - } - } - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - const std::string address = ExtractPath(sockets->first_addr()); - UnixEntry bind_entry; - ASSERT_TRUE(FindByPath(entries, &bind_entry, address)); - - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - entries = ASSERT_NO_ERRNO_AND_VALUE(ProcNetUnixEntries()); - - // Once again, we have no easy way to identify the connecting socket as it has - // no listed address. We can only identify the entry as the "non-bind socket - // entry" on gVisor, where we're guaranteed to have only the two entries we - // create during this test. - if (IsRunningOnGvisor()) { - ASSERT_EQ(entries.size(), 2); - UnixEntry connect_entry; - ASSERT_TRUE( - FindBy(entries, &connect_entry, [bind_entry](const UnixEntry& e) { - return e.inode != bind_entry.inode; - })); - EXPECT_EQ(connect_entry.state, SS_CONNECTING); - } -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/proc_pid_oomscore.cc b/test/syscalls/linux/proc_pid_oomscore.cc deleted file mode 100644 index 707821a3f..000000000 --- a/test/syscalls/linux/proc_pid_oomscore.cc +++ /dev/null @@ -1,72 +0,0 @@ -// 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 <errno.h> - -#include <exception> -#include <iostream> -#include <string> - -#include "test/util/fs_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -PosixErrorOr<int> ReadProcNumber(std::string path) { - ASSIGN_OR_RETURN_ERRNO(std::string contents, GetContents(path)); - EXPECT_EQ(contents[contents.length() - 1], '\n'); - - int num; - if (!absl::SimpleAtoi(contents, &num)) { - return PosixError(EINVAL, "invalid value: " + contents); - } - - return num; -} - -TEST(ProcPidOomscoreTest, BasicRead) { - auto const oom_score = - ASSERT_NO_ERRNO_AND_VALUE(ReadProcNumber("/proc/self/oom_score")); - EXPECT_LE(oom_score, 1000); - EXPECT_GE(oom_score, -1000); -} - -TEST(ProcPidOomscoreAdjTest, BasicRead) { - auto const oom_score = - ASSERT_NO_ERRNO_AND_VALUE(ReadProcNumber("/proc/self/oom_score_adj")); - - // oom_score_adj defaults to 0. - EXPECT_EQ(oom_score, 0); -} - -TEST(ProcPidOomscoreAdjTest, BasicWrite) { - constexpr int test_value = 7; - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/oom_score_adj", O_WRONLY)); - ASSERT_THAT( - RetryEINTR(write)(fd.get(), std::to_string(test_value).c_str(), 1), - SyscallSucceeds()); - - auto const oom_score = - ASSERT_NO_ERRNO_AND_VALUE(ReadProcNumber("/proc/self/oom_score_adj")); - EXPECT_EQ(oom_score, test_value); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/proc_pid_smaps.cc b/test/syscalls/linux/proc_pid_smaps.cc deleted file mode 100644 index 738923822..000000000 --- a/test/syscalls/linux/proc_pid_smaps.cc +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright 2019 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 <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <iostream> -#include <string> -#include <utility> -#include <vector> - -#include "absl/container/flat_hash_set.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_format.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/types/optional.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/proc_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::Contains; -using ::testing::ElementsAreArray; -using ::testing::IsSupersetOf; -using ::testing::Not; -using ::testing::Optional; - -namespace gvisor { -namespace testing { - -namespace { - -struct ProcPidSmapsEntry { - ProcMapsEntry maps_entry; - - // These fields should always exist, as they were included in e070ad49f311 - // "[PATCH] add /proc/pid/smaps". - size_t size_kb; - size_t rss_kb; - size_t shared_clean_kb; - size_t shared_dirty_kb; - size_t private_clean_kb; - size_t private_dirty_kb; - - // These fields were added later and may not be present. - absl::optional<size_t> pss_kb; - absl::optional<size_t> referenced_kb; - absl::optional<size_t> anonymous_kb; - absl::optional<size_t> anon_huge_pages_kb; - absl::optional<size_t> shared_hugetlb_kb; - absl::optional<size_t> private_hugetlb_kb; - absl::optional<size_t> swap_kb; - absl::optional<size_t> swap_pss_kb; - absl::optional<size_t> kernel_page_size_kb; - absl::optional<size_t> mmu_page_size_kb; - absl::optional<size_t> locked_kb; - - // Caution: "Note that there is no guarantee that every flag and associated - // mnemonic will be present in all further kernel releases. Things get - // changed, the flags may be vanished or the reverse -- new added." - Linux - // Documentation/filesystems/proc.txt, on VmFlags. Avoid checking for any - // flags that are not extremely well-established. - absl::optional<std::vector<std::string>> vm_flags; -}; - -// Given the value part of a /proc/[pid]/smaps field containing a value in kB -// (for example, " 4 kB", returns the value in kB (in this example, 4). -PosixErrorOr<size_t> SmapsValueKb(absl::string_view value) { - // TODO(jamieliu): let us use RE2 or <regex> - std::pair<absl::string_view, absl::string_view> parts = - absl::StrSplit(value, ' ', absl::SkipEmpty()); - if (parts.second != "kB") { - return PosixError(EINVAL, - absl::StrCat("invalid smaps field value: ", value)); - } - ASSIGN_OR_RETURN_ERRNO(auto val_kb, Atoi<size_t>(parts.first)); - return val_kb; -} - -PosixErrorOr<std::vector<ProcPidSmapsEntry>> ParseProcPidSmaps( - absl::string_view contents) { - std::vector<ProcPidSmapsEntry> entries; - absl::optional<ProcPidSmapsEntry> entry; - bool have_size_kb = false; - bool have_rss_kb = false; - bool have_shared_clean_kb = false; - bool have_shared_dirty_kb = false; - bool have_private_clean_kb = false; - bool have_private_dirty_kb = false; - - auto const finish_entry = [&] { - if (entry) { - if (!have_size_kb) { - return PosixError(EINVAL, "smaps entry is missing Size"); - } - if (!have_rss_kb) { - return PosixError(EINVAL, "smaps entry is missing Rss"); - } - if (!have_shared_clean_kb) { - return PosixError(EINVAL, "smaps entry is missing Shared_Clean"); - } - if (!have_shared_dirty_kb) { - return PosixError(EINVAL, "smaps entry is missing Shared_Dirty"); - } - if (!have_private_clean_kb) { - return PosixError(EINVAL, "smaps entry is missing Private_Clean"); - } - if (!have_private_dirty_kb) { - return PosixError(EINVAL, "smaps entry is missing Private_Dirty"); - } - // std::move(entry.value()) instead of std::move(entry).value(), because - // otherwise tools may report a "use-after-move" warning, which is - // spurious because entry.emplace() below resets entry to a new - // ProcPidSmapsEntry. - entries.emplace_back(std::move(entry.value())); - } - entry.emplace(); - have_size_kb = false; - have_rss_kb = false; - have_shared_clean_kb = false; - have_shared_dirty_kb = false; - have_private_clean_kb = false; - have_private_dirty_kb = false; - return NoError(); - }; - - // Holds key/value pairs from smaps field lines. Declared here so it can be - // captured by reference by the following lambdas. - std::vector<absl::string_view> key_value; - - auto const on_required_field_kb = [&](size_t* field, bool* have_field) { - if (*have_field) { - return PosixError( - EINVAL, - absl::StrFormat("smaps entry has duplicate %s line", key_value[0])); - } - ASSIGN_OR_RETURN_ERRNO(*field, SmapsValueKb(key_value[1])); - *have_field = true; - return NoError(); - }; - - auto const on_optional_field_kb = [&](absl::optional<size_t>* field) { - if (*field) { - return PosixError( - EINVAL, - absl::StrFormat("smaps entry has duplicate %s line", key_value[0])); - } - ASSIGN_OR_RETURN_ERRNO(*field, SmapsValueKb(key_value[1])); - return NoError(); - }; - - absl::flat_hash_set<std::string> unknown_fields; - auto const on_unknown_field = [&] { - absl::string_view key = key_value[0]; - // Don't mention unknown fields more than once. - if (unknown_fields.count(key)) { - return; - } - unknown_fields.insert(std::string(key)); - std::cerr << "skipping unknown smaps field " << key << std::endl; - }; - - auto lines = absl::StrSplit(contents, '\n', absl::SkipEmpty()); - for (absl::string_view l : lines) { - // Is this line a valid /proc/[pid]/maps entry? - auto maybe_maps_entry = ParseProcMapsLine(l); - if (maybe_maps_entry.ok()) { - // This marks the beginning of a new /proc/[pid]/smaps entry. - RETURN_IF_ERRNO(finish_entry()); - entry->maps_entry = std::move(maybe_maps_entry).ValueOrDie(); - continue; - } - // Otherwise it's a field in an existing /proc/[pid]/smaps entry of the form - // "key:value" (where value in practice will be preceded by a variable - // amount of whitespace). - if (!entry) { - std::cerr << "smaps line not considered a maps line: " - << maybe_maps_entry.error().message() << std::endl; - return PosixError( - EINVAL, - absl::StrCat("smaps field line without preceding maps line: ", l)); - } - key_value = absl::StrSplit(l, absl::MaxSplits(':', 1)); - if (key_value.size() != 2) { - return PosixError(EINVAL, absl::StrCat("invalid smaps field line: ", l)); - } - absl::string_view const key = key_value[0]; - if (key == "Size") { - RETURN_IF_ERRNO(on_required_field_kb(&entry->size_kb, &have_size_kb)); - } else if (key == "Rss") { - RETURN_IF_ERRNO(on_required_field_kb(&entry->rss_kb, &have_rss_kb)); - } else if (key == "Shared_Clean") { - RETURN_IF_ERRNO( - on_required_field_kb(&entry->shared_clean_kb, &have_shared_clean_kb)); - } else if (key == "Shared_Dirty") { - RETURN_IF_ERRNO( - on_required_field_kb(&entry->shared_dirty_kb, &have_shared_dirty_kb)); - } else if (key == "Private_Clean") { - RETURN_IF_ERRNO(on_required_field_kb(&entry->private_clean_kb, - &have_private_clean_kb)); - } else if (key == "Private_Dirty") { - RETURN_IF_ERRNO(on_required_field_kb(&entry->private_dirty_kb, - &have_private_dirty_kb)); - } else if (key == "Pss") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->pss_kb)); - } else if (key == "Referenced") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->referenced_kb)); - } else if (key == "Anonymous") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->anonymous_kb)); - } else if (key == "AnonHugePages") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->anon_huge_pages_kb)); - } else if (key == "Shared_Hugetlb") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->shared_hugetlb_kb)); - } else if (key == "Private_Hugetlb") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->private_hugetlb_kb)); - } else if (key == "Swap") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->swap_kb)); - } else if (key == "SwapPss") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->swap_pss_kb)); - } else if (key == "KernelPageSize") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->kernel_page_size_kb)); - } else if (key == "MMUPageSize") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->mmu_page_size_kb)); - } else if (key == "Locked") { - RETURN_IF_ERRNO(on_optional_field_kb(&entry->locked_kb)); - } else if (key == "VmFlags") { - if (entry->vm_flags) { - return PosixError(EINVAL, "duplicate VmFlags line"); - } - entry->vm_flags = absl::StrSplit(key_value[1], ' ', absl::SkipEmpty()); - } else { - on_unknown_field(); - } - } - RETURN_IF_ERRNO(finish_entry()); - return entries; -}; - -TEST(ParseProcPidSmapsTest, Correctness) { - auto entries = ASSERT_NO_ERRNO_AND_VALUE( - ParseProcPidSmaps("0-10000 rw-s 00000000 00:00 0 " - " /dev/zero (deleted)\n" - "Size: 0 kB\n" - "Rss: 1 kB\n" - "Pss: 2 kB\n" - "Shared_Clean: 3 kB\n" - "Shared_Dirty: 4 kB\n" - "Private_Clean: 5 kB\n" - "Private_Dirty: 6 kB\n" - "Referenced: 7 kB\n" - "Anonymous: 8 kB\n" - "AnonHugePages: 9 kB\n" - "Shared_Hugetlb: 10 kB\n" - "Private_Hugetlb: 11 kB\n" - "Swap: 12 kB\n" - "SwapPss: 13 kB\n" - "KernelPageSize: 14 kB\n" - "MMUPageSize: 15 kB\n" - "Locked: 16 kB\n" - "FutureUnknownKey: 17 kB\n" - "VmFlags: rd wr sh mr mw me ms lo ?? sd \n")); - ASSERT_EQ(entries.size(), 1); - auto& entry = entries[0]; - EXPECT_EQ(entry.maps_entry.filename, "/dev/zero (deleted)"); - EXPECT_EQ(entry.size_kb, 0); - EXPECT_EQ(entry.rss_kb, 1); - EXPECT_THAT(entry.pss_kb, Optional(2)); - EXPECT_EQ(entry.shared_clean_kb, 3); - EXPECT_EQ(entry.shared_dirty_kb, 4); - EXPECT_EQ(entry.private_clean_kb, 5); - EXPECT_EQ(entry.private_dirty_kb, 6); - EXPECT_THAT(entry.referenced_kb, Optional(7)); - EXPECT_THAT(entry.anonymous_kb, Optional(8)); - EXPECT_THAT(entry.anon_huge_pages_kb, Optional(9)); - EXPECT_THAT(entry.shared_hugetlb_kb, Optional(10)); - EXPECT_THAT(entry.private_hugetlb_kb, Optional(11)); - EXPECT_THAT(entry.swap_kb, Optional(12)); - EXPECT_THAT(entry.swap_pss_kb, Optional(13)); - EXPECT_THAT(entry.kernel_page_size_kb, Optional(14)); - EXPECT_THAT(entry.mmu_page_size_kb, Optional(15)); - EXPECT_THAT(entry.locked_kb, Optional(16)); - EXPECT_THAT(entry.vm_flags, - Optional(ElementsAreArray({"rd", "wr", "sh", "mr", "mw", "me", - "ms", "lo", "??", "sd"}))); -} - -// Returns the unique entry in entries containing the given address. -PosixErrorOr<ProcPidSmapsEntry> FindUniqueSmapsEntry( - std::vector<ProcPidSmapsEntry> const& entries, uintptr_t addr) { - auto const pred = [&](ProcPidSmapsEntry const& entry) { - return entry.maps_entry.start <= addr && addr < entry.maps_entry.end; - }; - auto const it = std::find_if(entries.begin(), entries.end(), pred); - if (it == entries.end()) { - return PosixError(EINVAL, - absl::StrFormat("no entry contains address %#x", addr)); - } - auto const it2 = std::find_if(it + 1, entries.end(), pred); - if (it2 != entries.end()) { - return PosixError( - EINVAL, - absl::StrFormat("overlapping entries [%#x-%#x) and [%#x-%#x) both " - "contain address %#x", - it->maps_entry.start, it->maps_entry.end, - it2->maps_entry.start, it2->maps_entry.end, addr)); - } - return *it; -} - -PosixErrorOr<std::vector<ProcPidSmapsEntry>> ReadProcSelfSmaps() { - ASSIGN_OR_RETURN_ERRNO(std::string contents, GetContents("/proc/self/smaps")); - return ParseProcPidSmaps(contents); -} - -TEST(ProcPidSmapsTest, SharedAnon) { - // Map with MAP_POPULATE so we get some RSS. - Mapping const m = ASSERT_NO_ERRNO_AND_VALUE(MmapAnon( - 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE)); - auto const entries = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfSmaps()); - auto const entry = - ASSERT_NO_ERRNO_AND_VALUE(FindUniqueSmapsEntry(entries, m.addr())); - - EXPECT_EQ(entry.size_kb, m.len() / 1024); - // It's possible that populated pages have been swapped out, so RSS might be - // less than size. - EXPECT_LE(entry.rss_kb, entry.size_kb); - - if (entry.pss_kb) { - // PSS should be exactly equal to RSS since no other address spaces should - // be sharing our new mapping. - EXPECT_EQ(entry.pss_kb.value(), entry.rss_kb); - } - - // "Shared" and "private" in smaps refers to whether or not *physical pages* - // are shared; thus all pages in our MAP_SHARED mapping should nevertheless - // be private. - EXPECT_EQ(entry.shared_clean_kb, 0); - EXPECT_EQ(entry.shared_dirty_kb, 0); - EXPECT_EQ(entry.private_clean_kb + entry.private_dirty_kb, entry.rss_kb) - << "Private_Clean = " << entry.private_clean_kb - << " kB, Private_Dirty = " << entry.private_dirty_kb << " kB"; - - // Shared anonymous mappings are implemented as a shmem file, so their pages - // are not PageAnon. - if (entry.anonymous_kb) { - EXPECT_EQ(entry.anonymous_kb.value(), 0); - } - - if (entry.vm_flags) { - EXPECT_THAT(entry.vm_flags.value(), - IsSupersetOf({"rd", "wr", "sh", "mr", "mw", "me", "ms"})); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("ex"))); - } -} - -TEST(ProcPidSmapsTest, PrivateAnon) { - // Map with MAP_POPULATE so we get some RSS. - Mapping const m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(2 * kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_POPULATE)); - auto const entries = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfSmaps()); - auto const entry = - ASSERT_NO_ERRNO_AND_VALUE(FindUniqueSmapsEntry(entries, m.addr())); - - // It's possible that our mapping was merged with another vma, so the smaps - // entry might be bigger than our original mapping. - EXPECT_GE(entry.size_kb, m.len() / 1024); - EXPECT_LE(entry.rss_kb, entry.size_kb); - if (entry.pss_kb) { - EXPECT_LE(entry.pss_kb.value(), entry.rss_kb); - } - - if (entry.anonymous_kb) { - EXPECT_EQ(entry.anonymous_kb.value(), entry.rss_kb); - } - - if (entry.vm_flags) { - EXPECT_THAT(entry.vm_flags.value(), IsSupersetOf({"wr", "mr", "mw", "me"})); - // We passed PROT_WRITE to mmap. On at least x86, the mapping is in - // practice readable because there is no way to configure the MMU to make - // pages writable but not readable. However, VmFlags should reflect the - // flags set on the VMA, so "rd" (VM_READ) should not appear in VmFlags. - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("rd"))); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("ex"))); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("sh"))); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("ms"))); - } -} - -TEST(ProcPidSmapsTest, SharedReadOnlyFile) { - size_t const kFileSize = kPageSize; - - auto const temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - ASSERT_THAT(truncate(temp_file.path().c_str(), kFileSize), SyscallSucceeds()); - auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); - - auto const m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kFileSize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd.get(), 0)); - auto const entries = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfSmaps()); - auto const entry = - ASSERT_NO_ERRNO_AND_VALUE(FindUniqueSmapsEntry(entries, m.addr())); - - // Most of the same logic as the SharedAnon case applies. - EXPECT_EQ(entry.size_kb, kFileSize / 1024); - EXPECT_LE(entry.rss_kb, entry.size_kb); - if (entry.pss_kb) { - EXPECT_EQ(entry.pss_kb.value(), entry.rss_kb); - } - EXPECT_EQ(entry.shared_clean_kb, 0); - EXPECT_EQ(entry.shared_dirty_kb, 0); - EXPECT_EQ(entry.private_clean_kb + entry.private_dirty_kb, entry.rss_kb) - << "Private_Clean = " << entry.private_clean_kb - << " kB, Private_Dirty = " << entry.private_dirty_kb << " kB"; - if (entry.anonymous_kb) { - EXPECT_EQ(entry.anonymous_kb.value(), 0); - } - - if (entry.vm_flags) { - EXPECT_THAT(entry.vm_flags.value(), IsSupersetOf({"rd", "mr", "me", "ms"})); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("wr"))); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("ex"))); - // Because the mapped file was opened O_RDONLY, the VMA is !VM_MAYWRITE and - // also !VM_SHARED. - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("sh"))); - EXPECT_THAT(entry.vm_flags.value(), Not(Contains("mw"))); - } -} - -// Tests that gVisor's /proc/[pid]/smaps provides all of the fields we expect it -// to, which as of this writing is all fields provided by Linux 4.4. -TEST(ProcPidSmapsTest, GvisorFields) { - SKIP_IF(!IsRunningOnGvisor()); - auto const entries = ASSERT_NO_ERRNO_AND_VALUE(ReadProcSelfSmaps()); - for (auto const& entry : entries) { - EXPECT_TRUE(entry.pss_kb); - EXPECT_TRUE(entry.referenced_kb); - EXPECT_TRUE(entry.anonymous_kb); - EXPECT_TRUE(entry.anon_huge_pages_kb); - EXPECT_TRUE(entry.shared_hugetlb_kb); - EXPECT_TRUE(entry.private_hugetlb_kb); - EXPECT_TRUE(entry.swap_kb); - EXPECT_TRUE(entry.swap_pss_kb); - EXPECT_THAT(entry.kernel_page_size_kb, Optional(kPageSize / 1024)); - EXPECT_THAT(entry.mmu_page_size_kb, Optional(kPageSize / 1024)); - EXPECT_TRUE(entry.locked_kb); - EXPECT_TRUE(entry.vm_flags); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/proc_pid_uid_gid_map.cc b/test/syscalls/linux/proc_pid_uid_gid_map.cc deleted file mode 100644 index af052a63c..000000000 --- a/test/syscalls/linux/proc_pid_uid_gid_map.cc +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright 2019 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 <fcntl.h> -#include <sched.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include <functional> -#include <string> -#include <tuple> -#include <utility> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/ascii.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_split.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" -#include "test/util/time_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<int> InNewUserNamespace(const std::function<void()>& fn) { - return InForkedProcess([&] { - TEST_PCHECK(unshare(CLONE_NEWUSER) == 0); - MaybeSave(); - fn(); - }); -} - -PosixErrorOr<std::tuple<pid_t, Cleanup>> CreateProcessInNewUserNamespace() { - int pipefd[2]; - if (pipe(pipefd) < 0) { - return PosixError(errno, "pipe failed"); - } - const auto cleanup_pipe_read = - Cleanup([&] { EXPECT_THAT(close(pipefd[0]), SyscallSucceeds()); }); - auto cleanup_pipe_write = - Cleanup([&] { EXPECT_THAT(close(pipefd[1]), SyscallSucceeds()); }); - pid_t child_pid = fork(); - if (child_pid < 0) { - return PosixError(errno, "fork failed"); - } - if (child_pid == 0) { - // Close our copy of the pipe's read end, which doesn't really matter. - TEST_PCHECK(close(pipefd[0]) >= 0); - TEST_PCHECK(unshare(CLONE_NEWUSER) == 0); - MaybeSave(); - // Indicate that we've switched namespaces by unblocking the parent's read. - TEST_PCHECK(close(pipefd[1]) >= 0); - while (true) { - SleepSafe(absl::Minutes(1)); - } - } - auto cleanup_child = Cleanup([child_pid] { - EXPECT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << "status = " << status; - }); - // Close our copy of the pipe's write end, then wait for the child to close - // its copy, indicating that it's switched namespaces. - cleanup_pipe_write.Release()(); - char buf; - if (RetryEINTR(read)(pipefd[0], &buf, 1) < 0) { - return PosixError(errno, "reading from pipe failed"); - } - MaybeSave(); - return std::make_tuple(child_pid, std::move(cleanup_child)); -} - -// TEST_CHECK-fails on error, since this function is used in contexts that -// require async-signal-safety. -void DenySetgroupsByPath(const char* path) { - int fd = open(path, O_WRONLY); - if (fd < 0 && errno == ENOENT) { - // On kernels where this file doesn't exist, writing "deny" to it isn't - // necessary to write to gid_map. - return; - } - TEST_PCHECK(fd >= 0); - MaybeSave(); - char deny[] = "deny"; - TEST_PCHECK(write(fd, deny, sizeof(deny)) == sizeof(deny)); - MaybeSave(); - TEST_PCHECK(close(fd) == 0); -} - -void DenySelfSetgroups() { DenySetgroupsByPath("/proc/self/setgroups"); } - -void DenyPidSetgroups(pid_t pid) { - DenySetgroupsByPath(absl::StrCat("/proc/", pid, "/setgroups").c_str()); -} - -// Returns a valid UID/GID that isn't id. -uint32_t another_id(uint32_t id) { return (id + 1) % 65535; } - -struct TestParam { - std::string desc; - int cap; - std::function<std::string(absl::string_view)> get_map_filename; - std::function<uint32_t()> get_current_id; -}; - -std::string DescribeTestParam(const ::testing::TestParamInfo<TestParam>& info) { - return info.param.desc; -} - -std::vector<TestParam> UidGidMapTestParams() { - return {TestParam{"UID", CAP_SETUID, - [](absl::string_view pid) { - return absl::StrCat("/proc/", pid, "/uid_map"); - }, - []() -> uint32_t { return getuid(); }}, - TestParam{"GID", CAP_SETGID, - [](absl::string_view pid) { - return absl::StrCat("/proc/", pid, "/gid_map"); - }, - []() -> uint32_t { return getgid(); }}}; -} - -class ProcUidGidMapTest : public ::testing::TestWithParam<TestParam> { - protected: - uint32_t CurrentID() { return GetParam().get_current_id(); } -}; - -class ProcSelfUidGidMapTest : public ProcUidGidMapTest { - protected: - PosixErrorOr<int> InNewUserNamespaceWithMapFD( - const std::function<void(int)>& fn) { - std::string map_filename = GetParam().get_map_filename("self"); - return InNewUserNamespace([&] { - int fd = open(map_filename.c_str(), O_RDWR); - TEST_PCHECK(fd >= 0); - MaybeSave(); - fn(fd); - TEST_PCHECK(close(fd) == 0); - }); - } -}; - -class ProcPidUidGidMapTest : public ProcUidGidMapTest { - protected: - PosixErrorOr<bool> HaveSetIDCapability() { - return HaveCapability(GetParam().cap); - } - - // Returns true if the caller is running in a user namespace with all IDs - // mapped. This matters for tests that expect to successfully map arbitrary - // IDs into a child user namespace, since even with CAP_SET*ID this is only - // possible if those IDs are mapped into the current one. - PosixErrorOr<bool> AllIDsMapped() { - ASSIGN_OR_RETURN_ERRNO(std::string id_map, - GetContents(GetParam().get_map_filename("self"))); - absl::StripTrailingAsciiWhitespace(&id_map); - std::vector<std::string> id_map_parts = - absl::StrSplit(id_map, ' ', absl::SkipEmpty()); - return id_map_parts == std::vector<std::string>({"0", "0", "4294967295"}); - } - - PosixErrorOr<FileDescriptor> OpenMapFile(pid_t pid) { - return Open(GetParam().get_map_filename(absl::StrCat(pid)), O_RDWR); - } -}; - -TEST_P(ProcSelfUidGidMapTest, IsInitiallyEmpty) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - EXPECT_THAT(InNewUserNamespaceWithMapFD([](int fd) { - char buf[64]; - TEST_PCHECK(read(fd, buf, sizeof(buf)) == 0); - }), - IsPosixErrorOkAndHolds(0)); -} - -TEST_P(ProcSelfUidGidMapTest, IdentityMapOwnID) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - uint32_t id = CurrentID(); - std::string line = absl::StrCat(id, " ", id, " 1"); - EXPECT_THAT( - InNewUserNamespaceWithMapFD([&](int fd) { - DenySelfSetgroups(); - TEST_PCHECK(static_cast<long unsigned int>( - write(fd, line.c_str(), line.size())) == line.size()); - }), - IsPosixErrorOkAndHolds(0)); -} - -TEST_P(ProcSelfUidGidMapTest, TrailingNewlineAndNULIgnored) { - // This is identical to IdentityMapOwnID, except that a trailing newline, NUL, - // and an invalid (incomplete) map entry are appended to the valid entry. The - // newline should be accepted, and everything after the NUL should be ignored. - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - uint32_t id = CurrentID(); - std::string line = absl::StrCat(id, " ", id, " 1\n\0 4 3"); - EXPECT_THAT( - InNewUserNamespaceWithMapFD([&](int fd) { - DenySelfSetgroups(); - // The write should return the full size of the write, even though - // characters after the NUL were ignored. - TEST_PCHECK(static_cast<long unsigned int>( - write(fd, line.c_str(), line.size())) == line.size()); - }), - IsPosixErrorOkAndHolds(0)); -} - -TEST_P(ProcSelfUidGidMapTest, NonIdentityMapOwnID) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - uint32_t id = CurrentID(); - uint32_t id2 = another_id(id); - std::string line = absl::StrCat(id2, " ", id, " 1"); - EXPECT_THAT( - InNewUserNamespaceWithMapFD([&](int fd) { - DenySelfSetgroups(); - TEST_PCHECK(static_cast<long unsigned int>( - write(fd, line.c_str(), line.size())) == line.size()); - }), - IsPosixErrorOkAndHolds(0)); -} - -TEST_P(ProcSelfUidGidMapTest, MapOtherID) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - // Whether or not we have CAP_SET*ID is irrelevant: the process running in the - // new (child) user namespace won't have any capabilities in the current - // (parent) user namespace, which is needed. - uint32_t id = CurrentID(); - uint32_t id2 = another_id(id); - std::string line = absl::StrCat(id, " ", id2, " 1"); - EXPECT_THAT(InNewUserNamespaceWithMapFD([&](int fd) { - DenySelfSetgroups(); - TEST_PCHECK(write(fd, line.c_str(), line.size()) < 0); - TEST_CHECK(errno == EPERM); - }), - IsPosixErrorOkAndHolds(0)); -} - -INSTANTIATE_TEST_SUITE_P(All, ProcSelfUidGidMapTest, - ::testing::ValuesIn(UidGidMapTestParams()), - DescribeTestParam); - -TEST_P(ProcPidUidGidMapTest, MapOtherIDPrivileged) { - // Like ProcSelfUidGidMapTest_MapOtherID, but since we have CAP_SET*ID in the - // parent user namespace (this one), we can map IDs that aren't ours. - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveSetIDCapability())); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(AllIDsMapped())); - - pid_t child_pid; - Cleanup cleanup_child; - std::tie(child_pid, cleanup_child) = - ASSERT_NO_ERRNO_AND_VALUE(CreateProcessInNewUserNamespace()); - - uint32_t id = CurrentID(); - uint32_t id2 = another_id(id); - std::string line = absl::StrCat(id, " ", id2, " 1"); - DenyPidSetgroups(child_pid); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenMapFile(child_pid)); - EXPECT_THAT(write(fd.get(), line.c_str(), line.size()), - SyscallSucceedsWithValue(line.size())); -} - -TEST_P(ProcPidUidGidMapTest, MapAnyIDsPrivileged) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanCreateUserNamespace())); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveSetIDCapability())); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(AllIDsMapped())); - - pid_t child_pid; - Cleanup cleanup_child; - std::tie(child_pid, cleanup_child) = - ASSERT_NO_ERRNO_AND_VALUE(CreateProcessInNewUserNamespace()); - - // Test all of: - // - // - Mapping ranges of length > 1 - // - // - Mapping multiple ranges - // - // - Non-identity mappings - char entries[] = "2 0 2\n4 6 2"; - DenyPidSetgroups(child_pid); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(OpenMapFile(child_pid)); - EXPECT_THAT(write(fd.get(), entries, sizeof(entries)), - SyscallSucceedsWithValue(sizeof(entries))); -} - -INSTANTIATE_TEST_SUITE_P(All, ProcPidUidGidMapTest, - ::testing::ValuesIn(UidGidMapTestParams()), - DescribeTestParam); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/processes.cc b/test/syscalls/linux/processes.cc deleted file mode 100644 index 412582515..000000000 --- a/test/syscalls/linux/processes.cc +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2021 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 <stdint.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "test/util/capability_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -int testSetPGIDOfZombie(void* arg) { - int p[2]; - - TEST_PCHECK(pipe(p) == 0); - - pid_t pid = fork(); - if (pid == 0) { - pid = fork(); - // Create a second child to repeat one of syzkaller reproducers. - if (pid == 0) { - pid = getpid(); - TEST_PCHECK(setpgid(pid, 0) == 0); - TEST_PCHECK(write(p[1], &pid, sizeof(pid)) == sizeof(pid)); - _exit(0); - } - TEST_PCHECK(pid > 0); - _exit(0); - } - close(p[1]); - TEST_PCHECK(pid > 0); - - // Get PID of the second child. - pid_t cpid; - TEST_PCHECK(read(p[0], &cpid, sizeof(cpid)) == sizeof(cpid)); - - // Wait when both child processes will die. - int c; - TEST_PCHECK(read(p[0], &c, sizeof(c)) == 0); - - // Wait the second child process to collect its zombie. - int status; - TEST_PCHECK(RetryEINTR(waitpid)(cpid, &status, 0) == cpid); - - // Set the child's group. - TEST_PCHECK(setpgid(pid, pid) == 0); - - TEST_PCHECK(RetryEINTR(waitpid)(-pid, &status, 0) == pid); - - TEST_PCHECK(status == 0); - _exit(0); -} - -TEST(Processes, SetPGIDOfZombie) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - // Fork a test process in a new PID namespace, because it needs to manipulate - // with reparanted processes. - struct clone_arg { - // Reserve some space for clone() to locate arguments and retcode in this - // place. - char stack[128] __attribute__((aligned(16))); - char stack_ptr[0]; - } ca; - pid_t pid; - ASSERT_THAT(pid = clone(testSetPGIDOfZombie, ca.stack_ptr, - CLONE_NEWPID | SIGCHLD, &ca), - SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_EQ(status, 0); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/pselect.cc b/test/syscalls/linux/pselect.cc deleted file mode 100644 index 4e43c4d7f..000000000 --- a/test/syscalls/linux/pselect.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/select.h> - -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/base_poll_test.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -struct MaskWithSize { - sigset_t* mask; - size_t mask_size; -}; - -// Linux and glibc have a different idea of the sizeof sigset_t. When calling -// the syscall directly, use what the kernel expects. -unsigned kSigsetSize = SIGRTMAX / 8; - -// Linux pselect(2) differs from the glibc wrapper function in that Linux -// updates the timeout with the amount of time remaining. In order to test this -// behavior we need to use the syscall directly. -int syscallPselect6(int nfds, fd_set* readfds, fd_set* writefds, - fd_set* exceptfds, struct timespec* timeout, - const MaskWithSize* mask_with_size) { - return syscall(SYS_pselect6, nfds, readfds, writefds, exceptfds, timeout, - mask_with_size); -} - -class PselectTest : public BasePollTest { - protected: - void SetUp() override { BasePollTest::SetUp(); } - void TearDown() override { BasePollTest::TearDown(); } -}; - -// See that when there are no FD sets, pselect behaves like sleep. -TEST_F(PselectTest, NullFds) { - struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); - ASSERT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, nullptr), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_nsec, 0); - - timeout = absl::ToTimespec(absl::Milliseconds(10)); - ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_nsec, 0); -} - -TEST_F(PselectTest, ClosedFds) { - fd_set read_set; - FD_ZERO(&read_set); - int fd; - ASSERT_THAT(fd = dup(1), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - FD_SET(fd, &read_set); - struct timespec timeout = absl::ToTimespec(absl::Milliseconds(10)); - EXPECT_THAT( - syscallPselect6(fd + 1, &read_set, nullptr, nullptr, &timeout, nullptr), - SyscallFailsWithErrno(EBADF)); -} - -TEST_F(PselectTest, ZeroTimeout) { - struct timespec timeout = {}; - ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_nsec, 0); -} - -// If random S/R interrupts the pselect, SIGALRM may be delivered before pselect -// restarts, causing the pselect to hang forever. -TEST_F(PselectTest, NoTimeout_NoRandomSave) { - // When there's no timeout, pselect may never return so set a timer. - SetTimer(absl::Milliseconds(100)); - // See that we get interrupted by the timer. - ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, nullptr, nullptr), - SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); -} - -TEST_F(PselectTest, InvalidTimeoutNegative) { - struct timespec timeout = absl::ToTimespec(absl::Seconds(-1)); - ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), - SyscallFailsWithErrno(EINVAL)); - EXPECT_EQ(timeout.tv_sec, -1); - EXPECT_EQ(timeout.tv_nsec, 0); -} - -TEST_F(PselectTest, InvalidTimeoutNotNormalized) { - struct timespec timeout = {0, 1000000001}; - ASSERT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, nullptr), - SyscallFailsWithErrno(EINVAL)); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_nsec, 1000000001); -} - -TEST_F(PselectTest, EmptySigMaskInvalidMaskSize) { - struct timespec timeout = {}; - MaskWithSize invalid = {nullptr, 7}; - EXPECT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, &invalid), - SyscallSucceeds()); -} - -TEST_F(PselectTest, EmptySigMaskValidMaskSize) { - struct timespec timeout = {}; - MaskWithSize invalid = {nullptr, 8}; - EXPECT_THAT(syscallPselect6(0, nullptr, nullptr, nullptr, &timeout, &invalid), - SyscallSucceeds()); -} - -TEST_F(PselectTest, InvalidMaskSize) { - struct timespec timeout = {}; - sigset_t sigmask; - ASSERT_THAT(sigemptyset(&sigmask), SyscallSucceeds()); - MaskWithSize invalid = {&sigmask, 7}; - EXPECT_THAT(syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &invalid), - SyscallFailsWithErrno(EINVAL)); -} - -// Verify that signals blocked by the pselect mask (that would otherwise be -// allowed) do not interrupt pselect. -TEST_F(PselectTest, SignalMaskBlocksSignal) { - absl::Duration duration(absl::Seconds(30)); - struct timespec timeout = absl::ToTimespec(duration); - absl::Duration timer_duration(absl::Seconds(10)); - - // Call with a mask that blocks SIGALRM. See that pselect is not interrupted - // (i.e. returns 0) and that upon completion, the timer has fired. - sigset_t mask; - ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); - ASSERT_THAT(sigaddset(&mask, SIGALRM), SyscallSucceeds()); - MaskWithSize mask_with_size = {&mask, kSigsetSize}; - SetTimer(timer_duration); - MaybeSave(); - ASSERT_FALSE(TimerFired()); - ASSERT_THAT( - syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &mask_with_size), - SyscallSucceeds()); - EXPECT_TRUE(TimerFired()); - EXPECT_EQ(absl::DurationFromTimespec(timeout), absl::Duration()); -} - -// Verify that signals allowed by the pselect mask (that would otherwise be -// blocked) interrupt pselect. -TEST_F(PselectTest, SignalMaskAllowsSignal) { - absl::Duration duration = absl::Seconds(30); - struct timespec timeout = absl::ToTimespec(duration); - absl::Duration timer_duration = absl::Seconds(10); - - sigset_t mask; - ASSERT_THAT(sigprocmask(0, nullptr, &mask), SyscallSucceeds()); - - // Block SIGALRM. - auto cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGALRM)); - - // Call with a mask that unblocks SIGALRM. See that pselect is interrupted. - MaskWithSize mask_with_size = {&mask, kSigsetSize}; - SetTimer(timer_duration); - MaybeSave(); - ASSERT_FALSE(TimerFired()); - ASSERT_THAT( - syscallPselect6(1, nullptr, nullptr, nullptr, &timeout, &mask_with_size), - SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); - EXPECT_GT(absl::DurationFromTimespec(timeout), absl::Duration()); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc deleted file mode 100644 index d1d7c6f84..000000000 --- a/test/syscalls/linux/ptrace.cc +++ /dev/null @@ -1,2399 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <elf.h> -#include <signal.h> -#include <stddef.h> -#include <sys/prctl.h> -#include <sys/ptrace.h> -#include <sys/socket.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/user.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <iostream> -#include <utility> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/fs_util.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/platform_util.h" -#include "test/util/signal_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/time_util.h" - -ABSL_FLAG(bool, ptrace_test_execve_child, false, - "If true, run the " - "PtraceExecveTest_Execve_GetRegs_PeekUser_SIGKILL_TraceClone_" - "TraceExit child workload."); -ABSL_FLAG(bool, ptrace_test_trace_descendants_allowed, false, - "If set, run the child workload for " - "PtraceTest_TraceDescendantsAllowed."); -ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_pid, false, - "If set, run the child workload for PtraceTest_PrctlSetPtracerPID."); -ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_any, false, - "If set, run the child workload for PtraceTest_PrctlSetPtracerAny."); -ABSL_FLAG(bool, ptrace_test_prctl_clear_ptracer, false, - "If set, run the child workload for PtraceTest_PrctlClearPtracer."); -ABSL_FLAG(bool, ptrace_test_prctl_replace_ptracer, false, - "If set, run the child workload for PtraceTest_PrctlReplacePtracer."); -ABSL_FLAG(int, ptrace_test_prctl_replace_ptracer_tid, -1, - "Specifies the replacement tracer tid in the child workload for " - "PtraceTest_PrctlReplacePtracer."); -ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_and_exit_tracee_thread, false, - "If set, run the child workload for " - "PtraceTest_PrctlSetPtracerPersistsPastTraceeThreadExit."); -ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_and_exec_non_leader, false, - "If set, run the child workload for " - "PtraceTest_PrctlSetPtracerDoesNotPersistPastNonLeaderExec."); -ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_and_exit_tracer_thread, false, - "If set, run the child workload for " - "PtraceTest_PrctlSetPtracerDoesNotPersistPastTracerThreadExit."); -ABSL_FLAG(int, ptrace_test_prctl_set_ptracer_and_exit_tracer_thread_tid, -1, - "Specifies the tracee tid in the child workload for " - "PtraceTest_PrctlSetPtracerDoesNotPersistPastTracerThreadExit."); -ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_respects_tracer_thread_id, false, - "If set, run the child workload for PtraceTest_PrctlSetPtracePID."); -ABSL_FLAG(int, ptrace_test_prctl_set_ptracer_respects_tracer_thread_id_tid, -1, - "Specifies the thread tid to be traced in the child workload " - "for PtraceTest_PrctlSetPtracerRespectsTracerThreadID."); - -ABSL_FLAG(bool, ptrace_test_tracee, false, - "If true, run the tracee process for the " - "PrctlSetPtracerDoesNotPersistPastLeaderExec and " - "PrctlSetPtracerDoesNotPersistPastNonLeaderExec workloads."); -ABSL_FLAG(int, ptrace_test_trace_tid, -1, - "If set, run a process to ptrace attach to the thread with the " - "specified pid for the PrctlSetPtracerRespectsTracerThreadID " - "workload."); -ABSL_FLAG(int, ptrace_test_fd, -1, - "Specifies the fd used for communication between tracer and tracee " - "processes across exec."); - -namespace gvisor { -namespace testing { - -namespace { - -// PTRACE_GETSIGMASK and PTRACE_SETSIGMASK are not defined until glibc 2.23 -// (fb53a27c5741 "Add new header definitions from Linux 4.4 (plus older ptrace -// definitions)"). -constexpr auto kPtraceGetSigMask = static_cast<__ptrace_request>(0x420a); -constexpr auto kPtraceSetSigMask = static_cast<__ptrace_request>(0x420b); - -// PTRACE_SYSEMU is not defined until glibc 2.27 (c48831d0eebf "linux/x86: sync -// sys/ptrace.h with Linux 4.14 [BZ #22433]"). -constexpr auto kPtraceSysemu = static_cast<__ptrace_request>(31); - -// PTRACE_EVENT_STOP is not defined until glibc 2.26 (3f67d1a7021e "Add Linux -// PTRACE_EVENT_STOP"). -constexpr int kPtraceEventStop = 128; - -// Sends sig to the current process with tgkill(2). -// -// glibc's raise(2) may change the signal mask before sending the signal. These -// extra syscalls make tests of syscall, signal interception, etc. difficult to -// write. -void RaiseSignal(int sig) { - pid_t pid = getpid(); - TEST_PCHECK(pid > 0); - pid_t tid = gettid(); - TEST_PCHECK(tid > 0); - TEST_PCHECK(tgkill(pid, tid, sig) == 0); -} - -constexpr char kYamaPtraceScopePath[] = "/proc/sys/kernel/yama/ptrace_scope"; - -// Returns the Yama ptrace scope. -PosixErrorOr<int> YamaPtraceScope() { - ASSIGN_OR_RETURN_ERRNO(bool exists, Exists(kYamaPtraceScopePath)); - if (!exists) { - // File doesn't exist means no Yama, so the scope is disabled -> 0. - return 0; - } - - std::string contents; - RETURN_IF_ERRNO(GetContents(kYamaPtraceScopePath, &contents)); - - int scope; - if (!absl::SimpleAtoi(contents, &scope)) { - return PosixError(EINVAL, absl::StrCat(contents, ": not a valid number")); - } - - return scope; -} - -int CheckPtraceAttach(pid_t pid) { - int ret = ptrace(PTRACE_ATTACH, pid, 0, 0); - MaybeSave(); - if (ret < 0) { - return ret; - } - - int status; - TEST_PCHECK(waitpid(pid, &status, 0) == pid); - MaybeSave(); - TEST_CHECK(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); - TEST_PCHECK(ptrace(PTRACE_DETACH, pid, 0, 0) == 0); - MaybeSave(); - return 0; -} - -TEST(PtraceTest, AttachSelf) { - EXPECT_THAT(ptrace(PTRACE_ATTACH, gettid(), 0, 0), - SyscallFailsWithErrno(EPERM)); -} - -TEST(PtraceTest, AttachSameThreadGroup) { - pid_t const tid = gettid(); - ScopedThread([&] { - EXPECT_THAT(ptrace(PTRACE_ATTACH, tid, 0, 0), SyscallFailsWithErrno(EPERM)); - }); -} - -TEST(PtraceTest, TraceParentNotAllowed) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) < 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - TEST_CHECK(CheckPtraceAttach(getppid()) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST(PtraceTest, TraceNonDescendantNotAllowed) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) < 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); - } - EXPECT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, TraceNonDescendantWithCapabilityAllowed) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_PTRACE))); - // Skip if disallowed by YAMA despite having CAP_SYS_PTRACE. - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) > 2); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, TraceDescendantsAllowed) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) > 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use socket pair to communicate tids to this process from its grandchild. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_trace_descendants_allowed", - "--ptrace_test_fd", std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - TEST_PCHECK(close(sockets[1]) == 0); - pid_t const grandchild_pid = fork(); - if (grandchild_pid == 0) { - // This test will create a new thread in the grandchild process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - TEST_PCHECK(grandchild_pid > 0); - MaybeSave(); - - // Wait for grandchild. Our parent process will kill it once it's done. - int status; - TEST_PCHECK(waitpid(grandchild_pid, &status, 0) == grandchild_pid); - TEST_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL); - MaybeSave(); - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - // We should be able to attach to any thread in the grandchild. - pid_t grandchild_tid1, grandchild_tid2; - ASSERT_THAT(read(sockets[1], &grandchild_tid1, sizeof(grandchild_tid1)), - SyscallSucceedsWithValue(sizeof(grandchild_tid1))); - ASSERT_THAT(read(sockets[1], &grandchild_tid2, sizeof(grandchild_tid2)), - SyscallSucceedsWithValue(sizeof(grandchild_tid2))); - - EXPECT_THAT(CheckPtraceAttach(grandchild_tid1), SyscallSucceeds()); - EXPECT_THAT(CheckPtraceAttach(grandchild_tid2), SyscallSucceeds()); - - // Clean up grandchild. - ASSERT_THAT(kill(grandchild_tid1, SIGKILL), SyscallSucceeds()); - - // Clean up child. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -[[noreturn]] void RunTraceDescendantsAllowed(int fd) { - // Let the tracer know our tid through the socket fd. - pid_t const tid = gettid(); - TEST_PCHECK(write(fd, &tid, sizeof(tid)) == sizeof(tid)); - MaybeSave(); - - ScopedThread t([fd] { - // See if any arbitrary thread (whose tid differs from the process id) can - // be traced as well. - pid_t const tid = gettid(); - TEST_PCHECK(write(fd, &tid, sizeof(tid)) == sizeof(tid)); - MaybeSave(); - while (true) { - SleepSafe(absl::Seconds(1)); - } - }); - - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -TEST(PtraceTest, PrctlSetPtracerInvalidPID) { - // EINVAL should also be returned if PR_SET_PTRACER is not supported. - EXPECT_THAT(prctl(PR_SET_PTRACER, 123456789), SyscallFailsWithErrno(EINVAL)); -} - -TEST(PtraceTest, PrctlSetPtracerPID) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_pid", - "--ptrace_test_fd", std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - TEST_PCHECK(close(sockets[1]) == 0); - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until tracee has called prctl. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlSetPtracerPID(int fd) { - ScopedThread t([fd] { - // Perform prctl in a separate thread to verify that it is process-wide. - TEST_PCHECK(prctl(PR_SET_PTRACER, getppid()) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(fd, "x", 1) == 1); - MaybeSave(); - }); - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -TEST(PtraceTest, PrctlSetPtracerAny) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_any", - "--ptrace_test_fd", std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - TEST_PCHECK(close(sockets[1]) == 0); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until tracee has called prctl. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlSetPtracerAny(int fd) { - ScopedThread t([fd] { - // Perform prctl in a separate thread to verify that it is process-wide. - TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(fd, "x", 1) == 1); - MaybeSave(); - }); - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -TEST(PtraceTest, PrctlClearPtracer) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_prctl_clear_ptracer", "--ptrace_test_fd", - std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - TEST_PCHECK(close(sockets[1]) == 0); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until tracee has called prctl. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlClearPtracer(int fd) { - ScopedThread t([fd] { - // Perform prctl in a separate thread to verify that it is process-wide. - TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0); - MaybeSave(); - TEST_PCHECK(prctl(PR_SET_PTRACER, 0) == 0); - MaybeSave(); - // Indicate that the prctl has been set/cleared. - TEST_PCHECK(write(fd, "x", 1) == 1); - MaybeSave(); - }); - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -TEST(PtraceTest, PrctlReplacePtracer) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - pid_t const unused_pid = fork(); - if (unused_pid == 0) { - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(unused_pid, SyscallSucceeds()); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", - "--ptrace_test_prctl_replace_ptracer", - "--ptrace_test_prctl_replace_ptracer_tid", - std::to_string(unused_pid), - "--ptrace_test_fd", - std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - TEST_PCHECK(close(sockets[1]) == 0); - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until tracee has called prctl. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; - - // Clean up unused. - ASSERT_THAT(kill(unused_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(unused_pid, &status, 0), - SyscallSucceedsWithValue(unused_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlReplacePtracer(int new_tracer_pid, int fd) { - TEST_PCHECK(prctl(PR_SET_PTRACER, getppid()) == 0); - MaybeSave(); - - ScopedThread t([new_tracer_pid, fd] { - TEST_PCHECK(prctl(PR_SET_PTRACER, new_tracer_pid) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(fd, "x", 1) == 1); - MaybeSave(); - }); - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -// Tests that YAMA exceptions store tracees by thread group leader. Exceptions -// are preserved even after the tracee thread exits, as long as the tracee's -// thread group leader is still around. -TEST(PtraceTest, PrctlSetPtracerPersistsPastTraceeThreadExit) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", - "--ptrace_test_prctl_set_ptracer_and_exit_tracee_thread", - "--ptrace_test_fd", std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - TEST_PCHECK(close(sockets[1]) == 0); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until the tracee thread calling prctl has terminated. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlSetPtracerPersistsPastTraceeThreadExit(int fd) { - ScopedThread t([] { - TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0); - MaybeSave(); - }); - t.Join(); - // Indicate that thread setting the prctl has exited. - TEST_PCHECK(write(fd, "x", 1) == 1); - MaybeSave(); - - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -// Tests that YAMA exceptions store tracees by thread group leader. Exceptions -// are preserved across exec as long as the thread group leader does not change, -// even if the tracee thread is terminated. -TEST(PtraceTest, PrctlSetPtracerPersistsPastLeaderExec) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_tracee", "--ptrace_test_fd", - std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - TEST_PCHECK(close(sockets[1]) == 0); - TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0); - MaybeSave(); - - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until the tracee has exec'd. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunTracee(int fd) { - // Indicate that we have exec'd. - TEST_PCHECK(write(fd, "x", 1) == 1); - MaybeSave(); - - while (true) { - SleepSafe(absl::Seconds(1)); - } -} - -// Tests that YAMA exceptions store tracees by thread group leader. Exceptions -// are cleared if the tracee process's thread group leader is terminated by -// exec. -TEST(PtraceTest, PrctlSetPtracerDoesNotPersistPastNonLeaderExec) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_and_exec_non_leader", - "--ptrace_test_fd", std::to_string(sockets[0])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - TEST_PCHECK(close(sockets[1]) == 0); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until the tracee has exec'd. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlSetPtracerDoesNotPersistPastNonLeaderExec(int fd) { - ScopedThread t([fd] { - TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0); - MaybeSave(); - - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_tracee", "--ptrace_test_fd", - std::to_string(fd)}; - char* const* const child_argv = owned_child_argv.get(); - - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - }); - t.Join(); - TEST_CHECK_MSG(false, "Survived execve? (main)"); - _exit(1); -} - -// Tests that YAMA exceptions store the tracer itself rather than the thread -// group leader. Exceptions are cleared when the tracer task exits, rather than -// when its thread group leader exits. -TEST(PtraceTest, PrctlSetPtracerDoesNotPersistPastTracerThreadExit) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - TEST_PCHECK(close(sockets[1]) == 0); - pid_t tracer_tid; - TEST_PCHECK(read(sockets[0], &tracer_tid, sizeof(tracer_tid)) == - sizeof(tracer_tid)); - MaybeSave(); - - TEST_PCHECK(prctl(PR_SET_PTRACER, tracer_tid) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(sockets[0], "x", 1) == 1); - MaybeSave(); - - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", - "--ptrace_test_prctl_set_ptracer_and_exit_tracer_thread", - "--ptrace_test_prctl_set_ptracer_and_exit_tracer_thread_tid", - std::to_string(tracee_pid), - "--ptrace_test_fd", - std::to_string(sockets[1])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlSetPtracerDoesNotPersistPastTracerThreadExit( - int tracee_tid, int fd) { - TEST_PCHECK(SetCapability(CAP_SYS_PTRACE, false).ok()); - - ScopedThread t([fd] { - pid_t const tracer_tid = gettid(); - TEST_PCHECK(write(fd, &tracer_tid, sizeof(tracer_tid)) == - sizeof(tracer_tid)); - - // Wait until the prctl has been set. - char done; - TEST_PCHECK(read(fd, &done, 1) == 1); - MaybeSave(); - }); - t.Join(); - - // Sleep for a bit before verifying the invalidation. The thread exit above - // should cause the ptrace exception to be invalidated, but in Linux, this is - // not done immediately. The YAMA exception is dropped during - // __put_task_struct(), which occurs (at the earliest) one RCU grace period - // after exit_notify() ==> release_task(). - SleepSafe(absl::Milliseconds(100)); - - TEST_CHECK(CheckPtraceAttach(tracee_tid) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); -} - -// Tests that YAMA exceptions store the tracer thread itself rather than the -// thread group leader. Exceptions are preserved across exec in the tracer -// thread, even if the thread group leader is terminated. -TEST(PtraceTest, PrctlSetPtracerRespectsTracerThreadID) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - TEST_PCHECK(close(sockets[1]) == 0); - pid_t tracer_tid; - TEST_PCHECK(read(sockets[0], &tracer_tid, sizeof(tracer_tid)) == - sizeof(tracer_tid)); - MaybeSave(); - - TEST_PCHECK(prctl(PR_SET_PTRACER, tracer_tid) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(sockets[0], "x", 1) == 1); - MaybeSave(); - - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - // Allocate vector before forking (not async-signal-safe). - ExecveArray const owned_child_argv = { - "/proc/self/exe", - "--ptrace_test_prctl_set_ptracer_respects_tracer_thread_id", - "--ptrace_test_prctl_set_ptracer_respects_tracer_thread_id_tid", - std::to_string(tracee_pid), - "--ptrace_test_fd", - std::to_string(sockets[1])}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // This test will create a new thread in the child process. - // pthread_create(2) isn't async-signal-safe, so we execve() first. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunPrctlSetPtracerRespectsTracerThreadID(int tracee_tid, - int fd) { - // Create a separate thread for tracing (i.e., not the thread group - // leader). After the subsequent execve(), the current thread group leader - // will no longer be exist, but the YAMA exception installed with this - // thread should still be valid. - ScopedThread t([tracee_tid, fd] { - pid_t const tracer_tid = gettid(); - TEST_PCHECK(write(fd, &tracer_tid, sizeof(tracer_tid))); - MaybeSave(); - - // Wait until the tracee has made the PR_SET_PTRACER prctl. - char done; - TEST_PCHECK(read(fd, &done, 1) == 1); - MaybeSave(); - - ExecveArray const owned_child_argv = { - "/proc/self/exe", "--ptrace_test_trace_tid", std::to_string(tracee_tid), - "--ptrace_test_fd", std::to_string(fd)}; - char* const* const child_argv = owned_child_argv.get(); - - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - }); - t.Join(); - TEST_CHECK_MSG(false, "Survived execve? (main)"); - _exit(1); -} - -[[noreturn]] void RunTraceTID(int tracee_tid, int fd) { - TEST_PCHECK(SetCapability(CAP_SYS_PTRACE, false).ok()); - TEST_PCHECK(CheckPtraceAttach(tracee_tid) == 0); - _exit(0); -} - -// Tests that removing a YAMA exception does not affect a tracer that is already -// attached. -TEST(PtraceTest, PrctlClearPtracerDoesNotAffectCurrentTracer) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Use sockets to synchronize between tracer and tracee. - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - TEST_PCHECK(close(sockets[1]) == 0); - TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(sockets[0], "x", 1) == 1); - MaybeSave(); - - // Wait until tracer has attached before clearing PR_SET_PTRACER. - char done; - TEST_PCHECK(read(sockets[0], &done, 1) == 1); - MaybeSave(); - - TEST_PCHECK(prctl(PR_SET_PTRACER, 0) == 0); - MaybeSave(); - // Indicate that the prctl has been set. - TEST_PCHECK(write(sockets[0], "x", 1) == 1); - MaybeSave(); - - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - ASSERT_THAT(close(sockets[0]), SyscallSucceeds()); - - std::string mem_path = "/proc/" + std::to_string(tracee_pid) + "/mem"; - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - // Wait until tracee has called prctl, or else we won't be able to attach. - char done; - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - - TEST_PCHECK(ptrace(PTRACE_ATTACH, tracee_pid, 0, 0) == 0); - MaybeSave(); - // Indicate that we have attached. - TEST_PCHECK(write(sockets[1], &done, 1) == 1); - MaybeSave(); - - // Block until tracee enters signal-delivery-stop as a result of the - // SIGSTOP sent by PTRACE_ATTACH. - int status; - TEST_PCHECK(waitpid(tracee_pid, &status, 0) == tracee_pid); - MaybeSave(); - TEST_CHECK(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); - MaybeSave(); - - TEST_PCHECK(ptrace(PTRACE_CONT, tracee_pid, 0, 0) == 0); - MaybeSave(); - - // Wait until tracee has cleared PR_SET_PTRACER. Even though it was cleared, - // we should still be able to access /proc/[pid]/mem because we are already - // attached. - TEST_PCHECK(read(sockets[1], &done, 1) == 1); - MaybeSave(); - TEST_PCHECK(open(mem_path.c_str(), O_RDONLY) != -1); - MaybeSave(); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, PrctlNotInherited) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - - // Allow any ptracer. This should not affect the child processes. - ASSERT_THAT(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY), SyscallSucceeds()); - - pid_t const tracee_pid = fork(); - if (tracee_pid == 0) { - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - ASSERT_THAT(tracee_pid, SyscallSucceeds()); - - pid_t const tracer_pid = fork(); - if (tracer_pid == 0) { - TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1); - TEST_PCHECK(errno == EPERM); - _exit(0); - } - ASSERT_THAT(tracer_pid, SyscallSucceeds()); - - // Clean up tracer. - int status; - ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Clean up tracee. - ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(tracee_pid, &status, 0), - SyscallSucceedsWithValue(tracee_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, AttachParent_PeekData_PokeData_SignalSuppression) { - // Yama prevents attaching to a parent. Skip the test if the scope is anything - // except disabled. - const int yama_scope = ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()); - SKIP_IF(yama_scope > 1); - if (yama_scope == 1) { - // Allow child to trace us. - ASSERT_THAT(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY), SyscallSucceeds()); - } - - // Test PTRACE_POKE/PEEKDATA on both anonymous and file mappings. - const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - ASSERT_NO_ERRNO(Truncate(file.path(), kPageSize)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - const auto file_mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap( - nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); - - constexpr long kBeforePokeDataAnonValue = 10; - constexpr long kAfterPokeDataAnonValue = 20; - constexpr long kBeforePokeDataFileValue = 0; // implicit, due to truncate() - constexpr long kAfterPokeDataFileValue = 30; - - volatile long anon_word = kBeforePokeDataAnonValue; - auto* file_word_ptr = static_cast<volatile long*>(file_mapping.ptr()); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Attach to the parent. - pid_t const parent_pid = getppid(); - TEST_PCHECK(ptrace(PTRACE_ATTACH, parent_pid, 0, 0) == 0); - MaybeSave(); - - // Block until the parent enters signal-delivery-stop as a result of the - // SIGSTOP sent by PTRACE_ATTACH. - int status; - TEST_PCHECK(waitpid(parent_pid, &status, 0) == parent_pid); - MaybeSave(); - TEST_CHECK(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); - - // Replace the value of anon_word in the parent process with - // kAfterPokeDataAnonValue. - long parent_word = ptrace(PTRACE_PEEKDATA, parent_pid, &anon_word, 0); - MaybeSave(); - TEST_CHECK(parent_word == kBeforePokeDataAnonValue); - TEST_PCHECK(ptrace(PTRACE_POKEDATA, parent_pid, &anon_word, - kAfterPokeDataAnonValue) == 0); - MaybeSave(); - - // Replace the value pointed to by file_word_ptr in the mapped file with - // kAfterPokeDataFileValue, via the parent process' mapping. - parent_word = ptrace(PTRACE_PEEKDATA, parent_pid, file_word_ptr, 0); - MaybeSave(); - TEST_CHECK(parent_word == kBeforePokeDataFileValue); - TEST_PCHECK(ptrace(PTRACE_POKEDATA, parent_pid, file_word_ptr, - kAfterPokeDataFileValue) == 0); - MaybeSave(); - - // Detach from the parent and suppress the SIGSTOP. If the SIGSTOP is not - // suppressed, the parent will hang in group-stop, causing the test to time - // out. - TEST_PCHECK(ptrace(PTRACE_DETACH, parent_pid, 0, 0) == 0); - MaybeSave(); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to complete. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Check that the child's PTRACE_POKEDATA was effective. - EXPECT_EQ(kAfterPokeDataAnonValue, anon_word); - EXPECT_EQ(kAfterPokeDataFileValue, *file_word_ptr); -} - -TEST(PtraceTest, GetSigMask) { - // glibc and the Linux kernel define a sigset_t with different sizes. To avoid - // creating a kernel_sigset_t and recreating all the modification functions - // (sigemptyset, etc), we just hardcode the kernel sigset size. - constexpr int kSizeofKernelSigset = 8; - constexpr int kBlockSignal = SIGUSR1; - sigset_t blocked; - sigemptyset(&blocked); - sigaddset(&blocked, kBlockSignal); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Install a signal handler for kBlockSignal to avoid termination and block - // it. - TEST_PCHECK(signal( - kBlockSignal, +[](int signo) {}) != SIG_ERR); - MaybeSave(); - TEST_PCHECK(sigprocmask(SIG_SETMASK, &blocked, nullptr) == 0); - MaybeSave(); - - // Enable tracing. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - - // This should be blocked. - RaiseSignal(kBlockSignal); - - // This should be suppressed by parent, who will change signal mask in the - // meantime, which means kBlockSignal should be delivered once this resumes. - RaiseSignal(SIGSTOP); - - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Get current signal mask. - sigset_t set; - EXPECT_THAT(ptrace(kPtraceGetSigMask, child_pid, kSizeofKernelSigset, &set), - SyscallSucceeds()); - EXPECT_THAT(blocked, EqualsSigset(set)); - - // Try to get current signal mask with bad size argument. - EXPECT_THAT(ptrace(kPtraceGetSigMask, child_pid, 0, nullptr), - SyscallFailsWithErrno(EINVAL)); - - // Try to set bad signal mask. - sigset_t* bad_addr = reinterpret_cast<sigset_t*>(-1); - EXPECT_THAT( - ptrace(kPtraceSetSigMask, child_pid, kSizeofKernelSigset, bad_addr), - SyscallFailsWithErrno(EFAULT)); - - // Set signal mask to empty set. - sigset_t set1; - sigemptyset(&set1); - EXPECT_THAT(ptrace(kPtraceSetSigMask, child_pid, kSizeofKernelSigset, &set1), - SyscallSucceeds()); - - // Suppress SIGSTOP and resume the child. It should re-enter - // signal-delivery-stop for kBlockSignal. - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == kBlockSignal) - << " status " << status; - - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - // Let's see that process exited normally. - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST(PtraceTest, GetSiginfo_SetSiginfo_SignalInjection) { - constexpr int kOriginalSigno = SIGUSR1; - constexpr int kInjectedSigno = SIGUSR2; - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Override all signal handlers. - struct sigaction sa = {}; - sa.sa_handler = +[](int signo) { _exit(signo); }; - TEST_PCHECK(sigfillset(&sa.sa_mask) == 0); - for (int signo = 1; signo < 32; signo++) { - if (signo == SIGKILL || signo == SIGSTOP) { - continue; - } - TEST_PCHECK(sigaction(signo, &sa, nullptr) == 0); - } - for (int signo = SIGRTMIN; signo <= SIGRTMAX; signo++) { - TEST_PCHECK(sigaction(signo, &sa, nullptr) == 0); - } - - // Unblock all signals. - TEST_PCHECK(sigprocmask(SIG_UNBLOCK, &sa.sa_mask, nullptr) == 0); - MaybeSave(); - - // Send ourselves kOriginalSignal while ptraced and exit with the signal we - // actually receive via the signal handler, if any, or 0 if we don't receive - // a signal. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - RaiseSignal(kOriginalSigno); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself kOriginalSigno and enter - // signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == kOriginalSigno) - << " status " << status; - - siginfo_t siginfo = {}; - ASSERT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), - SyscallSucceeds()); - EXPECT_EQ(kOriginalSigno, siginfo.si_signo); - EXPECT_EQ(SI_TKILL, siginfo.si_code); - - // Replace the signal with kInjectedSigno, and check that the child exits - // with kInjectedSigno, indicating that signal injection was successful. - siginfo.si_signo = kInjectedSigno; - ASSERT_THAT(ptrace(PTRACE_SETSIGINFO, child_pid, 0, &siginfo), - SyscallSucceeds()); - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, kInjectedSigno), - SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == kInjectedSigno) - << " status " << status; -} - -TEST(PtraceTest, SIGKILLDoesNotCauseSignalDeliveryStop) { - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - RaiseSignal(SIGKILL); - TEST_CHECK_MSG(false, "Survived SIGKILL?"); - _exit(1); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Expect the child to die to SIGKILL without entering signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, PtraceKill) { - constexpr int kOriginalSigno = SIGUSR1; - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - - // PTRACE_KILL only works if tracee has entered signal-delivery-stop. - RaiseSignal(kOriginalSigno); - TEST_CHECK_MSG(false, "Failed to kill the process?"); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself kOriginalSigno and enter - // signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == kOriginalSigno) - << " status " << status; - - ASSERT_THAT(ptrace(PTRACE_KILL, child_pid, 0, 0), SyscallSucceeds()); - - // Expect the child to die with SIGKILL. - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, GetRegSet) { - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Enable tracing. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - - // Use kill explicitly because we check the syscall argument register below. - kill(getpid(), SIGSTOP); - - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Get the general registers. - struct user_regs_struct regs; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &iov), - SyscallSucceeds()); - - // Read exactly the full register set. - EXPECT_EQ(iov.iov_len, sizeof(regs)); - -#if defined(__x86_64__) - // Child called kill(2), with SIGSTOP as arg 2. - EXPECT_EQ(regs.rsi, SIGSTOP); -#elif defined(__aarch64__) - EXPECT_EQ(regs.regs[1], SIGSTOP); -#endif - - // Suppress SIGSTOP and resume the child. - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - // Let's see that process exited normally. - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST(PtraceTest, AttachingConvertsGroupStopToPtraceStop) { - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - while (true) { - pause(); - } - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // SIGSTOP the child and wait for it to stop. - ASSERT_THAT(kill(child_pid, SIGSTOP), SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(child_pid, &status, WUNTRACED), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Attach to the child and expect it to re-enter a traced group-stop despite - // already being stopped. - ASSERT_THAT(ptrace(PTRACE_ATTACH, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Verify that the child is ptrace-stopped by checking that it can receive - // ptrace commands requiring a ptrace-stop. - EXPECT_THAT(ptrace(PTRACE_SETOPTIONS, child_pid, 0, 0), SyscallSucceeds()); - - // Group-stop is distinguished from signal-delivery-stop by PTRACE_GETSIGINFO - // failing with EINVAL. - siginfo_t siginfo = {}; - EXPECT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), - SyscallFailsWithErrno(EINVAL)); - - // Detach from the child and expect it to stay stopped without a notification. - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, WUNTRACED | WNOHANG), - SyscallSucceedsWithValue(0)); - - // Sending it SIGCONT should cause it to leave its stop. - ASSERT_THAT(kill(child_pid, SIGCONT), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, WCONTINUED), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFCONTINUED(status)) << " status " << status; - - // Clean up the child. - ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -// Fixture for tests parameterized by whether or not to use PTRACE_O_TRACEEXEC. -class PtraceExecveTest : public ::testing::TestWithParam<bool> { - protected: - bool TraceExec() const { return GetParam(); } -}; - -TEST_P(PtraceExecveTest, Execve_GetRegs_PeekUser_SIGKILL_TraceClone_TraceExit) { - ExecveArray const owned_child_argv = {"/proc/self/exe", - "--ptrace_test_execve_child"}; - char* const* const child_argv = owned_child_argv.get(); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. The test relies on calling execve() in a non-leader - // thread; pthread_create() isn't async-signal-safe, so the safest way to - // do this is to execve() first, then enable tracing and run the expected - // child process behavior in the new subprocess. - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve to test child"); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Enable PTRACE_O_TRACECLONE so we can get the ID of the child's non-leader - // thread, PTRACE_O_TRACEEXIT so we can observe the leader's death, and - // PTRACE_O_TRACEEXEC if required by the test. (The leader doesn't call - // execve, but options should be inherited across clone.) - long opts = PTRACE_O_TRACECLONE | PTRACE_O_TRACEEXIT; - if (TraceExec()) { - opts |= PTRACE_O_TRACEEXEC; - } - ASSERT_THAT(ptrace(PTRACE_SETOPTIONS, child_pid, 0, opts), SyscallSucceeds()); - - // Suppress the SIGSTOP and wait for the child's leader thread to report - // PTRACE_EVENT_CLONE. Get the new thread's ID from the event. - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_CLONE << 8), status >> 8); - unsigned long eventmsg; - ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, child_pid, 0, &eventmsg), - SyscallSucceeds()); - pid_t const nonleader_tid = eventmsg; - pid_t const leader_tid = child_pid; - - // The new thread should be ptraced and in signal-delivery-stop by SIGSTOP due - // to PTRACE_O_TRACECLONE. - // - // Before bf959931ddb88c4e4366e96dd22e68fa0db9527c "wait/ptrace: assume __WALL - // if the child is traced" (4.7) , waiting on it requires __WCLONE since, as a - // non-leader, its termination signal is 0. After, a standard wait is - // sufficient. - ASSERT_THAT(waitpid(nonleader_tid, &status, __WCLONE), - SyscallSucceedsWithValue(nonleader_tid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Resume both child threads. - for (pid_t const tid : {leader_tid, nonleader_tid}) { - ASSERT_THAT(ptrace(PTRACE_CONT, tid, 0, 0), SyscallSucceeds()); - } - - // The non-leader child thread should call execve, causing the leader thread - // to enter PTRACE_EVENT_EXIT with an apparent exit code of 0. At this point, - // the leader has not yet exited, so the non-leader should be blocked in - // execve. - ASSERT_THAT(waitpid(leader_tid, &status, 0), - SyscallSucceedsWithValue(leader_tid)); - EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_EXIT << 8), status >> 8); - ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, leader_tid, 0, &eventmsg), - SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(eventmsg) && WEXITSTATUS(eventmsg) == 0) - << " eventmsg " << eventmsg; - EXPECT_THAT(waitpid(nonleader_tid, &status, __WCLONE | WNOHANG), - SyscallSucceedsWithValue(0)); - - // Allow the leader to continue exiting. This should allow the non-leader to - // complete its execve, causing the original leader to be reaped without - // further notice and the non-leader to steal its ID. - ASSERT_THAT(ptrace(PTRACE_CONT, leader_tid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(leader_tid, &status, 0), - SyscallSucceedsWithValue(leader_tid)); - if (TraceExec()) { - // If PTRACE_O_TRACEEXEC was enabled, the execing thread should be in - // PTRACE_EVENT_EXEC-stop, with the event message set to its old thread ID. - EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_EXEC << 8), status >> 8); - ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, leader_tid, 0, &eventmsg), - SyscallSucceeds()); - EXPECT_EQ(nonleader_tid, eventmsg); - } else { - // Otherwise, the execing thread should have received SIGTRAP and should now - // be in signal-delivery-stop. - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << " status " << status; - } - -#ifdef __x86_64__ - { - // CS should be 0x33, indicating an 64-bit binary. - constexpr uint64_t kAMD64UserCS = 0x33; - EXPECT_THAT(ptrace(PTRACE_PEEKUSER, leader_tid, - offsetof(struct user_regs_struct, cs), 0), - SyscallSucceedsWithValue(kAMD64UserCS)); - struct user_regs_struct regs = {}; - ASSERT_THAT(ptrace(PTRACE_GETREGS, leader_tid, 0, ®s), - SyscallSucceeds()); - EXPECT_EQ(kAMD64UserCS, regs.cs); - } -#endif // defined(__x86_64__) - - // PTRACE_O_TRACEEXIT should have been inherited across execve. Send SIGKILL, - // which should end the PTRACE_EVENT_EXEC-stop or signal-delivery-stop and - // leave the child in PTRACE_EVENT_EXIT-stop. - ASSERT_THAT(kill(leader_tid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(leader_tid, &status, 0), - SyscallSucceedsWithValue(leader_tid)); - EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_EXIT << 8), status >> 8); - ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, leader_tid, 0, &eventmsg), - SyscallSucceeds()); - EXPECT_TRUE(WIFSIGNALED(eventmsg) && WTERMSIG(eventmsg) == SIGKILL) - << " eventmsg " << eventmsg; - - // End the PTRACE_EVENT_EXIT stop, allowing the child to exit. - ASSERT_THAT(ptrace(PTRACE_CONT, leader_tid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(leader_tid, &status, 0), - SyscallSucceedsWithValue(leader_tid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -[[noreturn]] void RunExecveChild() { - // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - RaiseSignal(SIGSTOP); - MaybeSave(); - - // Call execve() in a non-leader thread. As long as execve() succeeds, what - // exactly we execve() shouldn't really matter, since the tracer should kill - // us after execve() completes. - ScopedThread t([&] { - ExecveArray const owned_child_argv = {"/proc/self/exe", - "--this_flag_shouldnt_exist"}; - char* const* const child_argv = owned_child_argv.get(); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "Survived execve? (thread)"); - }); - t.Join(); - TEST_CHECK_MSG(false, "Survived execve? (main)"); - _exit(1); -} - -INSTANTIATE_TEST_SUITE_P(TraceExec, PtraceExecveTest, ::testing::Bool()); - -// This test has expectations on when syscall-enter/exit-stops occur that are -// violated if saving occurs, since saving interrupts all syscalls, causing -// premature syscall-exit. -TEST(PtraceTest, - ExitWhenParentIsNotTracer_Syscall_TraceVfork_TraceVforkDone_NoRandomSave) { - constexpr int kExitTraceeExitCode = 99; - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Block SIGCHLD so it doesn't interrupt wait4. - sigset_t mask; - TEST_PCHECK(sigemptyset(&mask) == 0); - TEST_PCHECK(sigaddset(&mask, SIGCHLD) == 0); - TEST_PCHECK(sigprocmask(SIG_SETMASK, &mask, nullptr) == 0); - MaybeSave(); - - // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - RaiseSignal(SIGSTOP); - MaybeSave(); - - // Spawn a vfork child that exits immediately, and reap it. Don't save - // after vfork since the parent expects to see wait4 as the next syscall. - pid_t const pid = vfork(); - if (pid == 0) { - _exit(kExitTraceeExitCode); - } - TEST_PCHECK_MSG(pid > 0, "vfork failed"); - - int status; - TEST_PCHECK(wait4(pid, &status, 0, nullptr) > 0); - MaybeSave(); - TEST_CHECK(WIFEXITED(status) && WEXITSTATUS(status) == kExitTraceeExitCode); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(child_pid, SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Enable PTRACE_O_TRACEVFORK so we can get the ID of the grandchild, - // PTRACE_O_TRACEVFORKDONE so we can observe PTRACE_EVENT_VFORK_DONE, and - // PTRACE_O_TRACESYSGOOD so syscall-enter/exit-stops are unambiguously - // indicated by a stop signal of SIGTRAP|0x80 rather than just SIGTRAP. - ASSERT_THAT(ptrace(PTRACE_SETOPTIONS, child_pid, 0, - PTRACE_O_TRACEVFORK | PTRACE_O_TRACEVFORKDONE | - PTRACE_O_TRACESYSGOOD), - SyscallSucceeds()); - - // Suppress the SIGSTOP and wait for the child to report PTRACE_EVENT_VFORK. - // Get the new process' ID from the event. - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_VFORK << 8), status >> 8); - unsigned long eventmsg; - ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, child_pid, 0, &eventmsg), - SyscallSucceeds()); - pid_t const grandchild_pid = eventmsg; - - // The grandchild should be traced by us and in signal-delivery-stop by - // SIGSTOP due to PTRACE_O_TRACEVFORK. This allows us to wait on it even - // though we're not its parent. - ASSERT_THAT(waitpid(grandchild_pid, &status, 0), - SyscallSucceedsWithValue(grandchild_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Resume the child with PTRACE_SYSCALL. Since the grandchild is still in - // signal-delivery-stop, the child should remain in vfork() waiting for the - // grandchild to exec or exit. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(1)); - ASSERT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - - // Suppress the grandchild's SIGSTOP and wait for the grandchild to exit. Pass - // WNOWAIT to waitid() so that we don't acknowledge the grandchild's exit yet. - ASSERT_THAT(ptrace(PTRACE_CONT, grandchild_pid, 0, 0), SyscallSucceeds()); - siginfo_t siginfo = {}; - ASSERT_THAT(waitid(P_PID, grandchild_pid, &siginfo, WEXITED | WNOWAIT), - SyscallSucceeds()); - EXPECT_EQ(SIGCHLD, siginfo.si_signo); - EXPECT_EQ(CLD_EXITED, siginfo.si_code); - EXPECT_EQ(kExitTraceeExitCode, siginfo.si_status); - EXPECT_EQ(grandchild_pid, siginfo.si_pid); - EXPECT_EQ(getuid(), siginfo.si_uid); - - // The child should now be in PTRACE_EVENT_VFORK_DONE stop. The event - // message should still be the grandchild's PID. - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGTRAP | (PTRACE_EVENT_VFORK_DONE << 8), status >> 8); - ASSERT_THAT(ptrace(PTRACE_GETEVENTMSG, child_pid, 0, &eventmsg), - SyscallSucceeds()); - EXPECT_EQ(grandchild_pid, eventmsg); - - // Resume the child with PTRACE_SYSCALL again and expect it to enter - // syscall-exit-stop for vfork() or clone(), either of which should return the - // grandchild's PID from the syscall. Aside from PTRACE_O_TRACESYSGOOD, - // syscall-stops are distinguished from signal-delivery-stop by - // PTRACE_GETSIGINFO returning a siginfo for which si_code == SIGTRAP or - // SIGTRAP|0x80. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) - << " status " << status; - ASSERT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), - SyscallSucceeds()); - EXPECT_TRUE(siginfo.si_code == SIGTRAP || siginfo.si_code == (SIGTRAP | 0x80)) - << "si_code = " << siginfo.si_code; - - { - struct user_regs_struct regs = {}; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &iov), - SyscallSucceeds()); -#if defined(__x86_64__) - EXPECT_TRUE(regs.orig_rax == SYS_vfork || regs.orig_rax == SYS_clone) - << "orig_rax = " << regs.orig_rax; - EXPECT_EQ(grandchild_pid, regs.rax); -#elif defined(__aarch64__) - EXPECT_TRUE(regs.regs[8] == SYS_clone) << "regs[8] = " << regs.regs[8]; - EXPECT_EQ(grandchild_pid, regs.regs[0]); -#endif // defined(__x86_64__) - } - - // After this point, the child will be making wait4 syscalls that will be - // interrupted by saving, so saving is not permitted. Note that this is - // explicitly released below once the grandchild exits. - DisableSave ds; - - // Resume the child with PTRACE_SYSCALL again and expect it to enter - // syscall-enter-stop for wait4(). - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) - << " status " << status; - ASSERT_THAT(ptrace(PTRACE_GETSIGINFO, child_pid, 0, &siginfo), - SyscallSucceeds()); - EXPECT_TRUE(siginfo.si_code == SIGTRAP || siginfo.si_code == (SIGTRAP | 0x80)) - << "si_code = " << siginfo.si_code; -#ifdef __x86_64__ - { - EXPECT_THAT(ptrace(PTRACE_PEEKUSER, child_pid, - offsetof(struct user_regs_struct, orig_rax), 0), - SyscallSucceedsWithValue(SYS_wait4)); - } -#endif // defined(__x86_64__) - - // Resume the child with PTRACE_SYSCALL again. Since the grandchild is - // waiting for the tracer (us) to acknowledge its exit first, wait4 should - // block. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(1)); - ASSERT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - - // Acknowledge the grandchild's exit. - ASSERT_THAT(waitpid(grandchild_pid, &status, 0), - SyscallSucceedsWithValue(grandchild_pid)); - ds.reset(); - - // Now the child should enter syscall-exit-stop for wait4, returning with the - // grandchild's PID. - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) - << " status " << status; - { - struct user_regs_struct regs = {}; - struct iovec iov; - iov.iov_base = ®s; - iov.iov_len = sizeof(regs); - EXPECT_THAT(ptrace(PTRACE_GETREGSET, child_pid, NT_PRSTATUS, &iov), - SyscallSucceeds()); -#if defined(__x86_64__) - EXPECT_EQ(SYS_wait4, regs.orig_rax); - EXPECT_EQ(grandchild_pid, regs.rax); -#elif defined(__aarch64__) - EXPECT_EQ(SYS_wait4, regs.regs[8]); - EXPECT_EQ(grandchild_pid, regs.regs[0]); -#endif // defined(__x86_64__) - } - - // Detach from the child and wait for it to exit. - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -// These tests requires knowledge of architecture-specific syscall convention. -#ifdef __x86_64__ -TEST(PtraceTest, Int3) { - SKIP_IF(PlatformSupportInt3() == PlatformSupport::NotSupported); - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Enable tracing. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - - // Interrupt 3 - trap to debugger - asm("int3"); - - _exit(56); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << " status " << status; - - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - - // The child should validate the injected return value and then exit normally. - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 56) - << " status " << status; -} - -TEST(PtraceTest, Sysemu_PokeUser) { - constexpr int kSysemuHelperFirstExitCode = 126; - constexpr uint64_t kSysemuInjectedExitGroupReturn = 42; - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - RaiseSignal(SIGSTOP); - - // Try to exit_group, expecting the tracer to skip the syscall and set its - // own return value. - int const rv = syscall(SYS_exit_group, kSysemuHelperFirstExitCode); - TEST_PCHECK_MSG(rv == kSysemuInjectedExitGroupReturn, - "exit_group returned incorrect value"); - - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Suppress the SIGSTOP and wait for the child to enter syscall-enter-stop - // for its first exit_group syscall. - ASSERT_THAT(ptrace(kPtraceSysemu, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << " status " << status; - - struct user_regs_struct regs = {}; - ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); - EXPECT_EQ(SYS_exit_group, regs.orig_rax); - EXPECT_EQ(-ENOSYS, regs.rax); - EXPECT_EQ(kSysemuHelperFirstExitCode, regs.rdi); - - // Replace the exit_group return value, then resume the child, which should - // automatically skip the syscall. - ASSERT_THAT( - ptrace(PTRACE_POKEUSER, child_pid, offsetof(struct user_regs_struct, rax), - kSysemuInjectedExitGroupReturn), - SyscallSucceeds()); - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - - // The child should validate the injected return value and then exit normally. - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -// This test also cares about syscall-exit-stop. -TEST(PtraceTest, ERESTART_NoRandomSave) { - constexpr int kSigno = SIGUSR1; - - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - - // Ignore, but unblock, kSigno. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - TEST_PCHECK(sigfillset(&sa.sa_mask) == 0); - TEST_PCHECK(sigaction(kSigno, &sa, nullptr) == 0); - MaybeSave(); - TEST_PCHECK(sigprocmask(SIG_UNBLOCK, &sa.sa_mask, nullptr) == 0); - MaybeSave(); - - // Enable tracing, then raise SIGSTOP and expect our parent to suppress it. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - RaiseSignal(SIGSTOP); - - // Invoke the pause syscall, which normally should not return until we - // receive a signal that "either terminates the process or causes the - // invocation of a signal-catching function". - pause(); - - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // After this point, the child's pause syscall will be interrupted by saving, - // so saving is not permitted. Note that this is explicitly released below - // once the child is stopped. - DisableSave ds; - - // Suppress the SIGSTOP and wait for the child to enter syscall-enter-stop for - // its pause syscall. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << " status " << status; - - struct user_regs_struct regs = {}; - ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); - EXPECT_EQ(SYS_pause, regs.orig_rax); - EXPECT_EQ(-ENOSYS, regs.rax); - - // Resume the child with PTRACE_SYSCALL and expect it to block in the pause - // syscall. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(1)); - ASSERT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - - // Send the child kSigno, causing it to return ERESTARTNOHAND and enter - // syscall-exit-stop from the pause syscall. - constexpr int ERESTARTNOHAND = 514; - ASSERT_THAT(kill(child_pid, kSigno), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) - << " status " << status; - ds.reset(); - - ASSERT_THAT(ptrace(PTRACE_GETREGS, child_pid, 0, ®s), SyscallSucceeds()); - EXPECT_EQ(SYS_pause, regs.orig_rax); - EXPECT_EQ(-ERESTARTNOHAND, regs.rax); - - // Replace the return value from pause with 0, causing pause to not be - // restarted despite kSigno being ignored. - ASSERT_THAT(ptrace(PTRACE_POKEUSER, child_pid, - offsetof(struct user_regs_struct, rax), 0), - SyscallSucceeds()); - - // Detach from the child and wait for it to exit. - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} -#endif // defined(__x86_64__) - -TEST(PtraceTest, Seize_Interrupt_Listen) { - volatile long child_should_spin = 1; - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - while (child_should_spin) { - SleepSafe(absl::Seconds(1)); - } - _exit(1); - } - - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Attach to the child with PTRACE_SEIZE; doing so should not stop the child. - ASSERT_THAT(ptrace(PTRACE_SEIZE, child_pid, 0, 0), SyscallSucceeds()); - int status; - EXPECT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - - // Stop the child with PTRACE_INTERRUPT. - ASSERT_THAT(ptrace(PTRACE_INTERRUPT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGTRAP | (kPtraceEventStop << 8), status >> 8); - - // Unset child_should_spin to verify that the child never leaves the spin - // loop. - ASSERT_THAT(ptrace(PTRACE_POKEDATA, child_pid, &child_should_spin, 0), - SyscallSucceeds()); - - // Send SIGSTOP to the child, then resume it, allowing it to proceed to - // signal-delivery-stop. - ASSERT_THAT(kill(child_pid, SIGSTOP), SyscallSucceeds()); - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // Release the child from signal-delivery-stop without suppressing the - // SIGSTOP, causing it to enter group-stop. - ASSERT_THAT(ptrace(PTRACE_CONT, child_pid, 0, SIGSTOP), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGSTOP | (kPtraceEventStop << 8), status >> 8); - - // "The state of the tracee after PTRACE_LISTEN is somewhat of a gray area: it - // is not in any ptrace-stop (ptrace commands won't work on it, and it will - // deliver waitpid(2) notifications), but it also may be considered 'stopped' - // because it is not executing instructions (is not scheduled), and if it was - // in group-stop before PTRACE_LISTEN, it will not respond to signals until - // SIGCONT is received." - ptrace(2). - ASSERT_THAT(ptrace(PTRACE_LISTEN, child_pid, 0, 0), SyscallSucceeds()); - EXPECT_THAT(ptrace(PTRACE_CONT, child_pid, 0, 0), - SyscallFailsWithErrno(ESRCH)); - EXPECT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(kill(child_pid, SIGTERM), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(1)); - EXPECT_THAT(waitpid(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - - // Send SIGCONT to the child, causing it to leave group-stop and re-trap due - // to PTRACE_LISTEN. - EXPECT_THAT(kill(child_pid, SIGCONT), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGTRAP | (kPtraceEventStop << 8), status >> 8); - - // Detach the child and expect it to exit due to the SIGTERM we sent while - // it was stopped by PTRACE_LISTEN. - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGTERM) - << " status " << status; -} - -TEST(PtraceTest, Interrupt_Listen_RequireSeize) { - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - raise(SIGSTOP); - _exit(0); - } - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Wait for the child to send itself SIGSTOP and enter signal-delivery-stop. - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP) - << " status " << status; - - // PTRACE_INTERRUPT and PTRACE_LISTEN should fail since the child wasn't - // attached with PTRACE_SEIZE, leaving the child in signal-delivery-stop. - EXPECT_THAT(ptrace(PTRACE_INTERRUPT, child_pid, 0, 0), - SyscallFailsWithErrno(EIO)); - EXPECT_THAT(ptrace(PTRACE_LISTEN, child_pid, 0, 0), - SyscallFailsWithErrno(EIO)); - - // Suppress SIGSTOP and detach from the child, expecting it to exit normally. - ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST(PtraceTest, SeizeSetOptions) { - pid_t const child_pid = fork(); - if (child_pid == 0) { - // In child process. - while (true) { - SleepSafe(absl::Seconds(1)); - } - } - - // In parent process. - ASSERT_THAT(child_pid, SyscallSucceeds()); - - // Attach to the child with PTRACE_SEIZE while setting PTRACE_O_TRACESYSGOOD. - ASSERT_THAT(ptrace(PTRACE_SEIZE, child_pid, 0, PTRACE_O_TRACESYSGOOD), - SyscallSucceeds()); - - // Stop the child with PTRACE_INTERRUPT. - ASSERT_THAT(ptrace(PTRACE_INTERRUPT, child_pid, 0, 0), SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_EQ(SIGTRAP | (kPtraceEventStop << 8), status >> 8); - - // Resume the child with PTRACE_SYSCALL and wait for it to enter - // syscall-enter-stop. The stop signal status from the syscall stop should be - // SIGTRAP|0x80, reflecting PTRACE_O_TRACESYSGOOD. - ASSERT_THAT(ptrace(PTRACE_SYSCALL, child_pid, 0, 0), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) - << " status " << status; - - // Clean up the child. - ASSERT_THAT(kill(child_pid, SIGKILL), SyscallSucceeds()); - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - if (WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)) { - // "SIGKILL kills even within system calls (syscall-exit-stop is not - // generated prior to death by SIGKILL). The net effect is that SIGKILL - // always kills the process (all its threads), even if some threads of the - // process are ptraced." - ptrace(2). This is technically true, but... - // - // When we send SIGKILL to the child, kernel/signal.c:complete_signal() => - // signal_wake_up(resume=1) kicks the tracee out of the syscall-enter-stop. - // The pending SIGKILL causes the syscall to be skipped, but the child - // thread still reports syscall-exit before checking for pending signals; in - // current kernels, this is - // arch/x86/entry/common.c:syscall_return_slowpath() => - // syscall_slow_exit_work() => - // include/linux/tracehook.h:tracehook_report_syscall_exit() => - // ptrace_report_syscall() => kernel/signal.c:ptrace_notify() => - // ptrace_do_notify() => ptrace_stop(). - // - // ptrace_stop() sets the task's state to TASK_TRACED and the task's - // exit_code to SIGTRAP|0x80 (passed by ptrace_report_syscall()), then calls - // freezable_schedule(). freezable_schedule() eventually reaches - // __schedule(), which detects signal_pending_state() due to the pending - // SIGKILL, sets the task's state back to TASK_RUNNING, and returns without - // descheduling. Thus, the task never enters syscall-exit-stop. However, if - // our wait4() => kernel/exit.c:wait_task_stopped() racily observes the - // TASK_TRACED state and the non-zero exit code set by ptrace_stop() before - // __schedule() sets the state back to TASK_RUNNING, it will return the - // task's exit_code as status W_STOPCODE(SIGTRAP|0x80). So we get a spurious - // syscall-exit-stop notification, and need to wait4() again for task exit. - // - // gVisor is not susceptible to this race because - // kernel.Task.waitCollectTraceeStopLocked() checks specifically for an - // active ptraceStop, which is not initiated if SIGKILL is pending. - std::cout << "Observed syscall-exit after SIGKILL" << std::endl; - ASSERT_THAT(waitpid(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - } - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) - << " status " << status; -} - -TEST(PtraceTest, SetYAMAPtraceScope) { - SKIP_IF(IsRunningWithVFS1()); - - // Do not modify the ptrace scope on the host. - SKIP_IF(!IsRunningOnGvisor()); - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(std::string(kYamaPtraceScopePath), O_RDWR)); - - ASSERT_THAT(write(fd.get(), "0", 1), SyscallSucceedsWithValue(1)); - - ASSERT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceeds()); - std::vector<char> buf(10); - EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), SyscallSucceeds()); - EXPECT_STREQ(buf.data(), "0\n"); - - // Test that a child can attach to its parent when ptrace_scope is 0. - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); - pid_t const child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(CheckPtraceAttach(getppid()) == 0); - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - - int status; - ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; - - // Set ptrace_scope back to 1 (and try writing with a newline). - ASSERT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceeds()); - ASSERT_THAT(write(fd.get(), "1\n", 2), SyscallSucceedsWithValue(2)); - - ASSERT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), SyscallSucceeds()); - EXPECT_STREQ(buf.data(), "1\n"); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_ptrace_test_execve_child)) { - gvisor::testing::RunExecveChild(); - } - - int fd = absl::GetFlag(FLAGS_ptrace_test_fd); - - if (absl::GetFlag(FLAGS_ptrace_test_trace_descendants_allowed)) { - gvisor::testing::RunTraceDescendantsAllowed(fd); - } - - if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_pid)) { - gvisor::testing::RunPrctlSetPtracerPID(fd); - } - - if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_any)) { - gvisor::testing::RunPrctlSetPtracerAny(fd); - } - - if (absl::GetFlag(FLAGS_ptrace_test_prctl_clear_ptracer)) { - gvisor::testing::RunPrctlClearPtracer(fd); - } - - if (absl::GetFlag(FLAGS_ptrace_test_prctl_replace_ptracer)) { - gvisor::testing::RunPrctlReplacePtracer( - absl::GetFlag(FLAGS_ptrace_test_prctl_replace_ptracer_tid), fd); - } - - if (absl::GetFlag( - FLAGS_ptrace_test_prctl_set_ptracer_and_exit_tracee_thread)) { - gvisor::testing::RunPrctlSetPtracerPersistsPastTraceeThreadExit(fd); - } - - if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_and_exec_non_leader)) { - gvisor::testing::RunPrctlSetPtracerDoesNotPersistPastNonLeaderExec( - fd); - } - - if (absl::GetFlag( - FLAGS_ptrace_test_prctl_set_ptracer_and_exit_tracer_thread)) { - gvisor::testing::RunPrctlSetPtracerDoesNotPersistPastTracerThreadExit( - absl::GetFlag( - FLAGS_ptrace_test_prctl_set_ptracer_and_exit_tracer_thread_tid), - fd); - } - - if (absl::GetFlag( - FLAGS_ptrace_test_prctl_set_ptracer_respects_tracer_thread_id)) { - gvisor::testing::RunPrctlSetPtracerRespectsTracerThreadID( - absl::GetFlag( - FLAGS_ptrace_test_prctl_set_ptracer_respects_tracer_thread_id_tid), - fd); - } - - if (absl::GetFlag(FLAGS_ptrace_test_tracee)) { - gvisor::testing::RunTracee(fd); - } - - int pid = absl::GetFlag(FLAGS_ptrace_test_trace_tid); - if (pid != -1) { - gvisor::testing::RunTraceTID(pid, fd); - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc deleted file mode 100644 index 8d15c491e..000000000 --- a/test/syscalls/linux/pty.cc +++ /dev/null @@ -1,1741 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <linux/capability.h> -#include <linux/major.h> -#include <poll.h> -#include <sched.h> -#include <signal.h> -#include <sys/ioctl.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/sysmacros.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <termios.h> -#include <unistd.h> - -#include <iostream> - -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "absl/strings/str_cat.h" -#include "absl/synchronization/notification.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/pty_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::AnyOf; -using ::testing::Contains; -using ::testing::Eq; -using ::testing::Not; -using SubprocessCallback = std::function<void()>; - -// Tests Unix98 pseudoterminals. -// -// These tests assume that /dev/ptmx exists and is associated with a devpts -// filesystem mounted at /dev/pts/. While a Linux distribution could -// theoretically place those anywhere, glibc expects those locations, so they -// are effectively fixed. - -// Minor device number for an unopened ptmx file. -constexpr int kPtmxMinor = 2; - -// The timeout when polling for data from a pty. When data is written to one end -// of a pty, Linux asynchronously makes it available to the other end, so we -// have to wait. -constexpr absl::Duration kTimeout = absl::Seconds(20); - -// The maximum line size in bytes returned per read from a pty file. -constexpr int kMaxLineSize = 4096; - -constexpr char kMasterPath[] = "/dev/ptmx"; - -// glibc defines its own, different, version of struct termios. We care about -// what the kernel does, not glibc. -#define KERNEL_NCCS 19 -struct kernel_termios { - tcflag_t c_iflag; - tcflag_t c_oflag; - tcflag_t c_cflag; - tcflag_t c_lflag; - cc_t c_line; - cc_t c_cc[KERNEL_NCCS]; -}; - -bool operator==(struct kernel_termios const& a, - struct kernel_termios const& b) { - return memcmp(&a, &b, sizeof(a)) == 0; -} - -// Returns the termios-style control character for the passed character. -// -// e.g., for Ctrl-C, i.e., ^C, call ControlCharacter('C'). -// -// Standard control characters are ASCII bytes 0 through 31. -constexpr char ControlCharacter(char c) { - // A is 1, B is 2, etc. - return c - 'A' + 1; -} - -// Returns the printable character the given control character represents. -constexpr char FromControlCharacter(char c) { return c + 'A' - 1; } - -// Returns true if c is a control character. -// -// Standard control characters are ASCII bytes 0 through 31. -constexpr bool IsControlCharacter(char c) { return c <= 31; } - -struct Field { - const char* name; - uint64_t mask; - uint64_t value; -}; - -// ParseFields returns a string representation of value, using the names in -// fields. -std::string ParseFields(const Field* fields, size_t len, uint64_t value) { - bool first = true; - std::string s; - for (size_t i = 0; i < len; i++) { - const Field f = fields[i]; - if ((value & f.mask) == f.value) { - if (!first) { - s += "|"; - } - s += f.name; - first = false; - value &= ~f.mask; - } - } - - if (value) { - if (!first) { - s += "|"; - } - absl::StrAppend(&s, value); - } - - return s; -} - -const Field kIflagFields[] = { - {"IGNBRK", IGNBRK, IGNBRK}, {"BRKINT", BRKINT, BRKINT}, - {"IGNPAR", IGNPAR, IGNPAR}, {"PARMRK", PARMRK, PARMRK}, - {"INPCK", INPCK, INPCK}, {"ISTRIP", ISTRIP, ISTRIP}, - {"INLCR", INLCR, INLCR}, {"IGNCR", IGNCR, IGNCR}, - {"ICRNL", ICRNL, ICRNL}, {"IUCLC", IUCLC, IUCLC}, - {"IXON", IXON, IXON}, {"IXANY", IXANY, IXANY}, - {"IXOFF", IXOFF, IXOFF}, {"IMAXBEL", IMAXBEL, IMAXBEL}, - {"IUTF8", IUTF8, IUTF8}, -}; - -const Field kOflagFields[] = { - {"OPOST", OPOST, OPOST}, {"OLCUC", OLCUC, OLCUC}, - {"ONLCR", ONLCR, ONLCR}, {"OCRNL", OCRNL, OCRNL}, - {"ONOCR", ONOCR, ONOCR}, {"ONLRET", ONLRET, ONLRET}, - {"OFILL", OFILL, OFILL}, {"OFDEL", OFDEL, OFDEL}, - {"NL0", NLDLY, NL0}, {"NL1", NLDLY, NL1}, - {"CR0", CRDLY, CR0}, {"CR1", CRDLY, CR1}, - {"CR2", CRDLY, CR2}, {"CR3", CRDLY, CR3}, - {"TAB0", TABDLY, TAB0}, {"TAB1", TABDLY, TAB1}, - {"TAB2", TABDLY, TAB2}, {"TAB3", TABDLY, TAB3}, - {"BS0", BSDLY, BS0}, {"BS1", BSDLY, BS1}, - {"FF0", FFDLY, FF0}, {"FF1", FFDLY, FF1}, - {"VT0", VTDLY, VT0}, {"VT1", VTDLY, VT1}, - {"XTABS", XTABS, XTABS}, -}; - -#ifndef IBSHIFT -// Shift from CBAUD to CIBAUD. -#define IBSHIFT 16 -#endif - -const Field kCflagFields[] = { - {"B0", CBAUD, B0}, - {"B50", CBAUD, B50}, - {"B75", CBAUD, B75}, - {"B110", CBAUD, B110}, - {"B134", CBAUD, B134}, - {"B150", CBAUD, B150}, - {"B200", CBAUD, B200}, - {"B300", CBAUD, B300}, - {"B600", CBAUD, B600}, - {"B1200", CBAUD, B1200}, - {"B1800", CBAUD, B1800}, - {"B2400", CBAUD, B2400}, - {"B4800", CBAUD, B4800}, - {"B9600", CBAUD, B9600}, - {"B19200", CBAUD, B19200}, - {"B38400", CBAUD, B38400}, - {"CS5", CSIZE, CS5}, - {"CS6", CSIZE, CS6}, - {"CS7", CSIZE, CS7}, - {"CS8", CSIZE, CS8}, - {"CSTOPB", CSTOPB, CSTOPB}, - {"CREAD", CREAD, CREAD}, - {"PARENB", PARENB, PARENB}, - {"PARODD", PARODD, PARODD}, - {"HUPCL", HUPCL, HUPCL}, - {"CLOCAL", CLOCAL, CLOCAL}, - {"B57600", CBAUD, B57600}, - {"B115200", CBAUD, B115200}, - {"B230400", CBAUD, B230400}, - {"B460800", CBAUD, B460800}, - {"B500000", CBAUD, B500000}, - {"B576000", CBAUD, B576000}, - {"B921600", CBAUD, B921600}, - {"B1000000", CBAUD, B1000000}, - {"B1152000", CBAUD, B1152000}, - {"B1500000", CBAUD, B1500000}, - {"B2000000", CBAUD, B2000000}, - {"B2500000", CBAUD, B2500000}, - {"B3000000", CBAUD, B3000000}, - {"B3500000", CBAUD, B3500000}, - {"B4000000", CBAUD, B4000000}, - {"CMSPAR", CMSPAR, CMSPAR}, - {"CRTSCTS", CRTSCTS, CRTSCTS}, - {"IB0", CIBAUD, B0 << IBSHIFT}, - {"IB50", CIBAUD, B50 << IBSHIFT}, - {"IB75", CIBAUD, B75 << IBSHIFT}, - {"IB110", CIBAUD, B110 << IBSHIFT}, - {"IB134", CIBAUD, B134 << IBSHIFT}, - {"IB150", CIBAUD, B150 << IBSHIFT}, - {"IB200", CIBAUD, B200 << IBSHIFT}, - {"IB300", CIBAUD, B300 << IBSHIFT}, - {"IB600", CIBAUD, B600 << IBSHIFT}, - {"IB1200", CIBAUD, B1200 << IBSHIFT}, - {"IB1800", CIBAUD, B1800 << IBSHIFT}, - {"IB2400", CIBAUD, B2400 << IBSHIFT}, - {"IB4800", CIBAUD, B4800 << IBSHIFT}, - {"IB9600", CIBAUD, B9600 << IBSHIFT}, - {"IB19200", CIBAUD, B19200 << IBSHIFT}, - {"IB38400", CIBAUD, B38400 << IBSHIFT}, - {"IB57600", CIBAUD, B57600 << IBSHIFT}, - {"IB115200", CIBAUD, B115200 << IBSHIFT}, - {"IB230400", CIBAUD, B230400 << IBSHIFT}, - {"IB460800", CIBAUD, B460800 << IBSHIFT}, - {"IB500000", CIBAUD, B500000 << IBSHIFT}, - {"IB576000", CIBAUD, B576000 << IBSHIFT}, - {"IB921600", CIBAUD, B921600 << IBSHIFT}, - {"IB1000000", CIBAUD, B1000000 << IBSHIFT}, - {"IB1152000", CIBAUD, B1152000 << IBSHIFT}, - {"IB1500000", CIBAUD, B1500000 << IBSHIFT}, - {"IB2000000", CIBAUD, B2000000 << IBSHIFT}, - {"IB2500000", CIBAUD, B2500000 << IBSHIFT}, - {"IB3000000", CIBAUD, B3000000 << IBSHIFT}, - {"IB3500000", CIBAUD, B3500000 << IBSHIFT}, - {"IB4000000", CIBAUD, B4000000 << IBSHIFT}, -}; - -const Field kLflagFields[] = { - {"ISIG", ISIG, ISIG}, {"ICANON", ICANON, ICANON}, - {"XCASE", XCASE, XCASE}, {"ECHO", ECHO, ECHO}, - {"ECHOE", ECHOE, ECHOE}, {"ECHOK", ECHOK, ECHOK}, - {"ECHONL", ECHONL, ECHONL}, {"NOFLSH", NOFLSH, NOFLSH}, - {"TOSTOP", TOSTOP, TOSTOP}, {"ECHOCTL", ECHOCTL, ECHOCTL}, - {"ECHOPRT", ECHOPRT, ECHOPRT}, {"ECHOKE", ECHOKE, ECHOKE}, - {"FLUSHO", FLUSHO, FLUSHO}, {"PENDIN", PENDIN, PENDIN}, - {"IEXTEN", IEXTEN, IEXTEN}, {"EXTPROC", EXTPROC, EXTPROC}, -}; - -std::string FormatCC(char c) { - if (isgraph(c)) { - return std::string(1, c); - } else if (c == ' ') { - return " "; - } else if (c == '\t') { - return "\\t"; - } else if (c == '\r') { - return "\\r"; - } else if (c == '\n') { - return "\\n"; - } else if (c == '\0') { - return "\\0"; - } else if (IsControlCharacter(c)) { - return absl::StrCat("^", std::string(1, FromControlCharacter(c))); - } - return absl::StrCat("\\x", absl::Hex(c)); -} - -std::ostream& operator<<(std::ostream& os, struct kernel_termios const& a) { - os << "{ c_iflag = " - << ParseFields(kIflagFields, ABSL_ARRAYSIZE(kIflagFields), a.c_iflag); - os << ", c_oflag = " - << ParseFields(kOflagFields, ABSL_ARRAYSIZE(kOflagFields), a.c_oflag); - os << ", c_cflag = " - << ParseFields(kCflagFields, ABSL_ARRAYSIZE(kCflagFields), a.c_cflag); - os << ", c_lflag = " - << ParseFields(kLflagFields, ABSL_ARRAYSIZE(kLflagFields), a.c_lflag); - os << ", c_line = " << a.c_line; - os << ", c_cc = { [VINTR] = '" << FormatCC(a.c_cc[VINTR]); - os << "', [VQUIT] = '" << FormatCC(a.c_cc[VQUIT]); - os << "', [VERASE] = '" << FormatCC(a.c_cc[VERASE]); - os << "', [VKILL] = '" << FormatCC(a.c_cc[VKILL]); - os << "', [VEOF] = '" << FormatCC(a.c_cc[VEOF]); - os << "', [VTIME] = '" << static_cast<int>(a.c_cc[VTIME]); - os << "', [VMIN] = " << static_cast<int>(a.c_cc[VMIN]); - os << ", [VSWTC] = '" << FormatCC(a.c_cc[VSWTC]); - os << "', [VSTART] = '" << FormatCC(a.c_cc[VSTART]); - os << "', [VSTOP] = '" << FormatCC(a.c_cc[VSTOP]); - os << "', [VSUSP] = '" << FormatCC(a.c_cc[VSUSP]); - os << "', [VEOL] = '" << FormatCC(a.c_cc[VEOL]); - os << "', [VREPRINT] = '" << FormatCC(a.c_cc[VREPRINT]); - os << "', [VDISCARD] = '" << FormatCC(a.c_cc[VDISCARD]); - os << "', [VWERASE] = '" << FormatCC(a.c_cc[VWERASE]); - os << "', [VLNEXT] = '" << FormatCC(a.c_cc[VLNEXT]); - os << "', [VEOL2] = '" << FormatCC(a.c_cc[VEOL2]); - os << "'}"; - return os; -} - -// Return the default termios settings for a new terminal. -struct kernel_termios DefaultTermios() { - struct kernel_termios t = {}; - t.c_iflag = IXON | ICRNL; - t.c_oflag = OPOST | ONLCR; - t.c_cflag = B38400 | CSIZE | CS8 | CREAD; - t.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN; - t.c_line = 0; - t.c_cc[VINTR] = ControlCharacter('C'); - t.c_cc[VQUIT] = ControlCharacter('\\'); - t.c_cc[VERASE] = '\x7f'; - t.c_cc[VKILL] = ControlCharacter('U'); - t.c_cc[VEOF] = ControlCharacter('D'); - t.c_cc[VTIME] = '\0'; - t.c_cc[VMIN] = 1; - t.c_cc[VSWTC] = '\0'; - t.c_cc[VSTART] = ControlCharacter('Q'); - t.c_cc[VSTOP] = ControlCharacter('S'); - t.c_cc[VSUSP] = ControlCharacter('Z'); - t.c_cc[VEOL] = '\0'; - t.c_cc[VREPRINT] = ControlCharacter('R'); - t.c_cc[VDISCARD] = ControlCharacter('O'); - t.c_cc[VWERASE] = ControlCharacter('W'); - t.c_cc[VLNEXT] = ControlCharacter('V'); - t.c_cc[VEOL2] = '\0'; - return t; -} - -// PollAndReadFd tries to read count bytes from buf within timeout. -// -// Returns a partial read if some bytes were read. -// -// fd must be non-blocking. -PosixErrorOr<size_t> PollAndReadFd(int fd, void* buf, size_t count, - absl::Duration timeout) { - absl::Time end = absl::Now() + timeout; - - size_t completed = 0; - absl::Duration remaining; - while ((remaining = end - absl::Now()) > absl::ZeroDuration()) { - struct pollfd pfd = {fd, POLLIN, 0}; - int ret = RetryEINTR(poll)(&pfd, 1, absl::ToInt64Milliseconds(remaining)); - if (ret < 0) { - return PosixError(errno, "poll failed"); - } else if (ret == 0) { - // Timed out. - continue; - } else if (ret != 1) { - return PosixError(EINVAL, absl::StrCat("Bad poll ret ", ret)); - } - - ssize_t n = - ReadFd(fd, static_cast<char*>(buf) + completed, count - completed); - if (n < 0) { - if (errno == EAGAIN) { - // Linux sometimes returns EAGAIN from this read, despite the fact that - // poll returned success. Let's just do what do as we are told and try - // again. - continue; - } - return PosixError(errno, "read failed"); - } - completed += n; - if (completed >= count) { - return completed; - } - } - - if (completed) { - return completed; - } - return PosixError(ETIMEDOUT, "Poll timed out"); -} - -TEST(PtyTrunc, Truncate) { - // Opening PTYs with O_TRUNC shouldn't cause an error, but calls to - // (f)truncate should. - FileDescriptor master = - ASSERT_NO_ERRNO_AND_VALUE(Open(kMasterPath, O_RDWR | O_TRUNC)); - int n = ASSERT_NO_ERRNO_AND_VALUE(ReplicaID(master)); - std::string spath = absl::StrCat("/dev/pts/", n); - FileDescriptor replica = - ASSERT_NO_ERRNO_AND_VALUE(Open(spath, O_RDWR | O_NONBLOCK | O_TRUNC)); - - EXPECT_THAT(truncate(kMasterPath, 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(truncate(spath.c_str(), 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(ftruncate(master.get(), 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(ftruncate(replica.get(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(BasicPtyTest, StatUnopenedMaster) { - struct stat s; - ASSERT_THAT(stat(kMasterPath, &s), SyscallSucceeds()); - - EXPECT_EQ(s.st_rdev, makedev(TTYAUX_MAJOR, kPtmxMinor)); - EXPECT_EQ(s.st_size, 0); - EXPECT_EQ(s.st_blocks, 0); - - // ptmx attached to a specific devpts mount uses block size 1024. See - // fs/devpts/inode.c:devpts_fill_super. - // - // The global ptmx device uses the block size of the filesystem it is created - // on (which is usually 4096 for disk filesystems). - EXPECT_THAT(s.st_blksize, AnyOf(Eq(1024), Eq(4096))); -} - -// Waits for count bytes to be readable from fd. Unlike poll, which can return -// before all data is moved into a pty's read buffer, this function waits for -// all count bytes to become readable. -PosixErrorOr<int> WaitUntilReceived(int fd, int count) { - int buffered = -1; - absl::Duration remaining; - absl::Time end = absl::Now() + kTimeout; - while ((remaining = end - absl::Now()) > absl::ZeroDuration()) { - if (ioctl(fd, FIONREAD, &buffered) < 0) { - return PosixError(errno, "failed FIONREAD ioctl"); - } - if (buffered >= count) { - return buffered; - } - absl::SleepFor(absl::Milliseconds(500)); - } - return PosixError( - ETIMEDOUT, - absl::StrFormat( - "FIONREAD timed out, receiving only %d of %d expected bytes", - buffered, count)); -} - -// Verifies that there is nothing left to read from fd. -void ExpectFinished(const FileDescriptor& fd) { - // Nothing more to read. - char c; - EXPECT_THAT(ReadFd(fd.get(), &c, 1), SyscallFailsWithErrno(EAGAIN)); -} - -// Verifies that we can read expected bytes from fd into buf. -void ExpectReadable(const FileDescriptor& fd, int expected, char* buf) { - size_t n = ASSERT_NO_ERRNO_AND_VALUE( - PollAndReadFd(fd.get(), buf, expected, kTimeout)); - EXPECT_EQ(expected, n); -} - -TEST(BasicPtyTest, OpenMasterReplica) { - FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master)); -} - -TEST(BasicPtyTest, OpenSetsControllingTTY) { - SKIP_IF(IsRunningWithVFS1()); - // setsid either puts us in a new session or fails because we're already the - // session leader. Either way, this ensures we're the session leader. - setsid(); - - // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - struct sigaction old_sa; - ASSERT_THAT(sigaction(SIGHUP, &sa, &old_sa), SyscallSucceeds()); - auto cleanup = Cleanup([old_sa] { - EXPECT_THAT(sigaction(SIGHUP, &old_sa, NULL), SyscallSucceeds()); - }); - - FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - FileDescriptor replica = - ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master, O_NONBLOCK | O_RDWR)); - - // Opening replica should make it our controlling TTY, and therefore we are - // able to give it up. - ASSERT_THAT(ioctl(replica.get(), TIOCNOTTY), SyscallSucceeds()); -} - -TEST(BasicPtyTest, OpenMasterDoesNotSetsControllingTTY) { - SKIP_IF(IsRunningWithVFS1()); - // setsid either puts us in a new session or fails because we're already the - // session leader. Either way, this ensures we're the session leader. - setsid(); - FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - - // Opening master does not set the controlling TTY, and therefore we are - // unable to give it up. - ASSERT_THAT(ioctl(master.get(), TIOCNOTTY), SyscallFailsWithErrno(ENOTTY)); -} - -TEST(BasicPtyTest, OpenNOCTTY) { - SKIP_IF(IsRunningWithVFS1()); - // setsid either puts us in a new session or fails because we're already the - // session leader. Either way, this ensures we're the session leader. - setsid(); - FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE( - OpenReplica(master, O_NOCTTY | O_NONBLOCK | O_RDWR)); - - // Opening replica with O_NOCTTY won't make it our controlling TTY, and - // therefore we are unable to give it up. - ASSERT_THAT(ioctl(replica.get(), TIOCNOTTY), SyscallFailsWithErrno(ENOTTY)); -} - -// The replica entry in /dev/pts/ disappears when the master is closed, even if -// the replica is still open. -TEST(BasicPtyTest, ReplicaEntryGoneAfterMasterClose) { - FileDescriptor master = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master)); - - // Get pty index. - int index = -1; - ASSERT_THAT(ioctl(master.get(), TIOCGPTN, &index), SyscallSucceeds()); - - std::string path = absl::StrCat("/dev/pts/", index); - - struct stat st; - EXPECT_THAT(stat(path.c_str(), &st), SyscallSucceeds()); - - master.reset(); - - EXPECT_THAT(stat(path.c_str(), &st), SyscallFailsWithErrno(ENOENT)); -} - -TEST(BasicPtyTest, Getdents) { - FileDescriptor master1 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - int index1 = -1; - ASSERT_THAT(ioctl(master1.get(), TIOCGPTN, &index1), SyscallSucceeds()); - FileDescriptor replica1 = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master1)); - - FileDescriptor master2 = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR)); - int index2 = -1; - ASSERT_THAT(ioctl(master2.get(), TIOCGPTN, &index2), SyscallSucceeds()); - FileDescriptor replica2 = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master2)); - - // The directory contains ptmx, index1, and index2. (Plus any additional PTYs - // unrelated to this test.) - - std::vector<std::string> contents = - ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev/pts/", true)); - EXPECT_THAT(contents, Contains(absl::StrCat(index1))); - EXPECT_THAT(contents, Contains(absl::StrCat(index2))); - - master2.reset(); - - // The directory contains ptmx and index1, but not index2 since the master is - // closed. (Plus any additional PTYs unrelated to this test.) - - contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir("/dev/pts/", true)); - EXPECT_THAT(contents, Contains(absl::StrCat(index1))); - EXPECT_THAT(contents, Not(Contains(absl::StrCat(index2)))); - - // N.B. devpts supports legacy "single-instance" mode and new "multi-instance" - // mode. In legacy mode, devpts does not contain a "ptmx" device (the distro - // must use mknod to create it somewhere, presumably /dev/ptmx). - // Multi-instance mode does include a "ptmx" device tied to that mount. - // - // We don't check for the presence or absence of "ptmx", as distros vary in - // their usage of the two modes. -} - -class PtyTest : public ::testing::Test { - protected: - void SetUp() override { - master_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK)); - replica_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master_)); - } - - void DisableCanonical() { - struct kernel_termios t = {}; - EXPECT_THAT(ioctl(replica_.get(), TCGETS, &t), SyscallSucceeds()); - t.c_lflag &= ~ICANON; - EXPECT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - } - - void EnableCanonical() { - struct kernel_termios t = {}; - EXPECT_THAT(ioctl(replica_.get(), TCGETS, &t), SyscallSucceeds()); - t.c_lflag |= ICANON; - EXPECT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - } - - // Master and replica ends of the PTY. Non-blocking. - FileDescriptor master_; - FileDescriptor replica_; -}; - -// Master to replica sanity test. -TEST_F(PtyTest, WriteMasterToReplica) { - // N.B. by default, the replica reads nothing until the master writes a - // newline. - constexpr char kBuf[] = "hello\n"; - - EXPECT_THAT(WriteFd(master_.get(), kBuf, sizeof(kBuf) - 1), - SyscallSucceedsWithValue(sizeof(kBuf) - 1)); - - // Linux moves data from the master to the replica via async work scheduled - // via tty_flip_buffer_push. Since it is asynchronous, the data may not be - // available for reading immediately. Instead we must poll and assert that it - // becomes available "soon". - - char buf[sizeof(kBuf)] = {}; - ExpectReadable(replica_, sizeof(buf) - 1, buf); - - EXPECT_EQ(memcmp(buf, kBuf, sizeof(kBuf)), 0); -} - -// Replica to master sanity test. -TEST_F(PtyTest, WriteReplicaToMaster) { - // N.B. by default, the master reads nothing until the replica writes a - // newline, and the master gets a carriage return. - constexpr char kInput[] = "hello\n"; - constexpr char kExpected[] = "hello\r\n"; - - EXPECT_THAT(WriteFd(replica_.get(), kInput, sizeof(kInput) - 1), - SyscallSucceedsWithValue(sizeof(kInput) - 1)); - - // Linux moves data from the master to the replica via async work scheduled - // via tty_flip_buffer_push. Since it is asynchronous, the data may not be - // available for reading immediately. Instead we must poll and assert that it - // becomes available "soon". - - char buf[sizeof(kExpected)] = {}; - ExpectReadable(master_, sizeof(buf) - 1, buf); - - EXPECT_EQ(memcmp(buf, kExpected, sizeof(kExpected)), 0); -} - -TEST_F(PtyTest, WriteInvalidUTF8) { - char c = 0xff; - ASSERT_THAT(syscall(__NR_write, master_.get(), &c, sizeof(c)), - SyscallSucceedsWithValue(sizeof(c))); -} - -// Both the master and replica report the standard default termios settings. -// -// Note that TCGETS on the master actually redirects to the replica (see comment -// on MasterTermiosUnchangable). -TEST_F(PtyTest, DefaultTermios) { - struct kernel_termios t = {}; - EXPECT_THAT(ioctl(replica_.get(), TCGETS, &t), SyscallSucceeds()); - EXPECT_EQ(t, DefaultTermios()); - - EXPECT_THAT(ioctl(master_.get(), TCGETS, &t), SyscallSucceeds()); - EXPECT_EQ(t, DefaultTermios()); -} - -// Changing termios from the master actually affects the replica. -// -// TCSETS on the master actually redirects to the replica (see comment on -// MasterTermiosUnchangable). -TEST_F(PtyTest, TermiosAffectsReplica) { - struct kernel_termios master_termios = {}; - EXPECT_THAT(ioctl(master_.get(), TCGETS, &master_termios), SyscallSucceeds()); - master_termios.c_lflag ^= ICANON; - EXPECT_THAT(ioctl(master_.get(), TCSETS, &master_termios), SyscallSucceeds()); - - struct kernel_termios replica_termios = {}; - EXPECT_THAT(ioctl(replica_.get(), TCGETS, &replica_termios), - SyscallSucceeds()); - EXPECT_EQ(master_termios, replica_termios); -} - -// The master end of the pty has termios: -// -// struct kernel_termios t = { -// .c_iflag = 0; -// .c_oflag = 0; -// .c_cflag = B38400 | CS8 | CREAD; -// .c_lflag = 0; -// .c_cc = /* same as DefaultTermios */ -// } -// -// (From drivers/tty/pty.c:unix98_pty_init) -// -// All termios control ioctls on the master actually redirect to the replica -// (drivers/tty/tty_ioctl.c:tty_mode_ioctl), making it impossible to change the -// master termios. -// -// Verify this by setting ICRNL (which rewrites input \r to \n) and verify that -// it has no effect on the master. -TEST_F(PtyTest, MasterTermiosUnchangable) { - struct kernel_termios master_termios = {}; - EXPECT_THAT(ioctl(master_.get(), TCGETS, &master_termios), SyscallSucceeds()); - master_termios.c_lflag |= ICRNL; - EXPECT_THAT(ioctl(master_.get(), TCSETS, &master_termios), SyscallSucceeds()); - - char c = '\r'; - ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - ExpectReadable(master_, 1, &c); - EXPECT_EQ(c, '\r'); // ICRNL had no effect! - - ExpectFinished(master_); -} - -// ICRNL rewrites input \r to \n. -TEST_F(PtyTest, TermiosICRNL) { - struct kernel_termios t = DefaultTermios(); - t.c_iflag |= ICRNL; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - char c = '\r'; - ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - ExpectReadable(replica_, 1, &c); - EXPECT_EQ(c, '\n'); - - ExpectFinished(replica_); -} - -// ONLCR rewrites output \n to \r\n. -TEST_F(PtyTest, TermiosONLCR) { - struct kernel_termios t = DefaultTermios(); - t.c_oflag |= ONLCR; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - char c = '\n'; - ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - // Extra byte for NUL for EXPECT_STREQ. - char buf[3] = {}; - ExpectReadable(master_, 2, buf); - EXPECT_STREQ(buf, "\r\n"); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, TermiosIGNCR) { - struct kernel_termios t = DefaultTermios(); - t.c_iflag |= IGNCR; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - char c = '\r'; - ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - // Nothing to read. - ASSERT_THAT(PollAndReadFd(replica_.get(), &c, 1, kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); -} - -// Test that we can successfully poll for readable data from the replica. -TEST_F(PtyTest, TermiosPollReplica) { - struct kernel_termios t = DefaultTermios(); - t.c_iflag |= IGNCR; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - absl::Notification notify; - int sfd = replica_.get(); - ScopedThread th([sfd, ¬ify]() { - notify.Notify(); - - // Poll on the reader fd with POLLIN event. - struct pollfd poll_fd = {sfd, POLLIN, 0}; - EXPECT_THAT( - RetryEINTR(poll)(&poll_fd, 1, absl::ToInt64Milliseconds(kTimeout)), - SyscallSucceedsWithValue(1)); - - // Should trigger POLLIN event. - EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); - }); - - notify.WaitForNotification(); - // Sleep ensures that poll begins waiting before we write to the FD. - absl::SleepFor(absl::Seconds(1)); - - char s[] = "foo\n"; - ASSERT_THAT(WriteFd(master_.get(), s, strlen(s) + 1), SyscallSucceeds()); -} - -// Test that we can successfully poll for readable data from the master. -TEST_F(PtyTest, TermiosPollMaster) { - struct kernel_termios t = DefaultTermios(); - t.c_iflag |= IGNCR; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(master_.get(), TCSETS, &t), SyscallSucceeds()); - - absl::Notification notify; - int mfd = master_.get(); - ScopedThread th([mfd, ¬ify]() { - notify.Notify(); - - // Poll on the reader fd with POLLIN event. - struct pollfd poll_fd = {mfd, POLLIN, 0}; - EXPECT_THAT( - RetryEINTR(poll)(&poll_fd, 1, absl::ToInt64Milliseconds(kTimeout)), - SyscallSucceedsWithValue(1)); - - // Should trigger POLLIN event. - EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); - }); - - notify.WaitForNotification(); - // Sleep ensures that poll begins waiting before we write to the FD. - absl::SleepFor(absl::Seconds(1)); - - char s[] = "foo\n"; - ASSERT_THAT(WriteFd(replica_.get(), s, strlen(s) + 1), SyscallSucceeds()); -} - -TEST_F(PtyTest, TermiosINLCR) { - struct kernel_termios t = DefaultTermios(); - t.c_iflag |= INLCR; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - char c = '\n'; - ASSERT_THAT(WriteFd(master_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - ExpectReadable(replica_, 1, &c); - EXPECT_EQ(c, '\r'); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, TermiosONOCR) { - struct kernel_termios t = DefaultTermios(); - t.c_oflag |= ONOCR; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - // The terminal is at column 0, so there should be no CR to read. - char c = '\r'; - ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - // Nothing to read. - ASSERT_THAT(PollAndReadFd(master_.get(), &c, 1, kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); - - // This time the column is greater than 0, so we should be able to read the CR - // out of the other end. - constexpr char kInput[] = "foo\r"; - constexpr int kInputSize = sizeof(kInput) - 1; - ASSERT_THAT(WriteFd(replica_.get(), kInput, kInputSize), - SyscallSucceedsWithValue(kInputSize)); - - char buf[kInputSize] = {}; - ExpectReadable(master_, kInputSize, buf); - - EXPECT_EQ(memcmp(buf, kInput, kInputSize), 0); - - ExpectFinished(master_); - - // Terminal should be at column 0 again, so no CR can be read. - ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - // Nothing to read. - ASSERT_THAT(PollAndReadFd(master_.get(), &c, 1, kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); -} - -TEST_F(PtyTest, TermiosOCRNL) { - struct kernel_termios t = DefaultTermios(); - t.c_oflag |= OCRNL; - t.c_lflag &= ~ICANON; // for byte-by-byte reading. - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - - // The terminal is at column 0, so there should be no CR to read. - char c = '\r'; - ASSERT_THAT(WriteFd(replica_.get(), &c, 1), SyscallSucceedsWithValue(1)); - - ExpectReadable(master_, 1, &c); - EXPECT_EQ(c, '\n'); - - ExpectFinished(master_); -} - -// Tests that VEOL is disabled when we start, and that we can set it to enable -// it. -TEST_F(PtyTest, VEOLTermination) { - // Write a few bytes ending with '\0', and confirm that we can't read. - constexpr char kInput[] = "hello"; - ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), - SyscallSucceedsWithValue(sizeof(kInput))); - char buf[sizeof(kInput)] = {}; - ASSERT_THAT(PollAndReadFd(replica_.get(), buf, sizeof(kInput), kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); - - // Set the EOL character to '=' and write it. - constexpr char delim = '='; - struct kernel_termios t = DefaultTermios(); - t.c_cc[VEOL] = delim; - ASSERT_THAT(ioctl(replica_.get(), TCSETS, &t), SyscallSucceeds()); - ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); - - // Now we can read, as sending EOL caused the line to become available. - ExpectReadable(replica_, sizeof(kInput), buf); - EXPECT_EQ(memcmp(buf, kInput, sizeof(kInput)), 0); - - ExpectReadable(replica_, 1, buf); - EXPECT_EQ(buf[0], '='); - - ExpectFinished(replica_); -} - -// Tests that we can write more than the 4096 character limit, then a -// terminating character, then read out just the first 4095 bytes plus the -// terminator. -TEST_F(PtyTest, CanonBigWrite) { - constexpr int kWriteLen = kMaxLineSize + 4; - char input[kWriteLen]; - memset(input, 'M', kWriteLen - 1); - input[kWriteLen - 1] = '\n'; - ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen), - SyscallSucceedsWithValue(kWriteLen)); - - // We can read the line. - char buf[kMaxLineSize] = {}; - ExpectReadable(replica_, kMaxLineSize, buf); - - ExpectFinished(replica_); -} - -// Tests that data written in canonical mode can be read immediately once -// switched to noncanonical mode. -TEST_F(PtyTest, SwitchCanonToNoncanon) { - // Write a few bytes without a terminating character, switch to noncanonical - // mode, and read them. - constexpr char kInput[] = "hello"; - ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), - SyscallSucceedsWithValue(sizeof(kInput))); - - // Nothing available yet. - char buf[sizeof(kInput)] = {}; - ASSERT_THAT(PollAndReadFd(replica_.get(), buf, sizeof(kInput), kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); - - DisableCanonical(); - - ExpectReadable(replica_, sizeof(kInput), buf); - EXPECT_STREQ(buf, kInput); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, SwitchCanonToNonCanonNewline) { - // Write a few bytes with a terminating character. - constexpr char kInput[] = "hello\n"; - ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput)), - SyscallSucceedsWithValue(sizeof(kInput))); - - DisableCanonical(); - - // We can read the line. - char buf[sizeof(kInput)] = {}; - ExpectReadable(replica_, sizeof(kInput), buf); - EXPECT_STREQ(buf, kInput); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, SwitchNoncanonToCanonNewlineBig) { - DisableCanonical(); - - // Write more than the maximum line size, then write a delimiter. - constexpr int kWriteLen = 4100; - char input[kWriteLen]; - memset(input, 'M', kWriteLen); - ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen), - SyscallSucceedsWithValue(kWriteLen)); - // Wait for the input queue to fill. - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kMaxLineSize - 1)); - constexpr char delim = '\n'; - ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); - - EnableCanonical(); - - // We can read the line. - char buf[kMaxLineSize] = {}; - ExpectReadable(replica_, kMaxLineSize - 1, buf); - - // We can also read the remaining characters. - ExpectReadable(replica_, 6, buf); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, SwitchNoncanonToCanonNoNewline) { - DisableCanonical(); - - // Write a few bytes without a terminating character. - // mode, and read them. - constexpr char kInput[] = "hello"; - ASSERT_THAT(WriteFd(master_.get(), kInput, sizeof(kInput) - 1), - SyscallSucceedsWithValue(sizeof(kInput) - 1)); - - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), sizeof(kInput) - 1)); - EnableCanonical(); - - // We can read the line. - char buf[sizeof(kInput)] = {}; - ExpectReadable(replica_, sizeof(kInput) - 1, buf); - EXPECT_STREQ(buf, kInput); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, SwitchNoncanonToCanonNoNewlineBig) { - DisableCanonical(); - - // Write a few bytes without a terminating character. - // mode, and read them. - constexpr int kWriteLen = 4100; - char input[kWriteLen]; - memset(input, 'M', kWriteLen); - ASSERT_THAT(WriteFd(master_.get(), input, kWriteLen), - SyscallSucceedsWithValue(kWriteLen)); - - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kMaxLineSize - 1)); - EnableCanonical(); - - // We can read the line. - char buf[kMaxLineSize] = {}; - ExpectReadable(replica_, kMaxLineSize - 1, buf); - - ExpectFinished(replica_); -} - -// Tests that we can write over the 4095 noncanonical limit, then read out -// everything. -TEST_F(PtyTest, NoncanonBigWrite) { - DisableCanonical(); - - // Write well over the 4095 internal buffer limit. - constexpr char kInput = 'M'; - constexpr int kInputSize = kMaxLineSize * 2; - for (int i = 0; i < kInputSize; i++) { - // This makes too many syscalls for save/restore. - const DisableSave ds; - ASSERT_THAT(WriteFd(master_.get(), &kInput, sizeof(kInput)), - SyscallSucceedsWithValue(sizeof(kInput))); - } - - // We should be able to read out everything. Sleep a bit so that Linux has a - // chance to move data from the master to the replica. - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kMaxLineSize - 1)); - for (int i = 0; i < kInputSize; i++) { - // This makes too many syscalls for save/restore. - const DisableSave ds; - char c; - ExpectReadable(replica_, 1, &c); - ASSERT_EQ(c, kInput); - } - - ExpectFinished(replica_); -} - -// ICANON doesn't make input available until a line delimiter is typed. -// -// Test newline. -TEST_F(PtyTest, TermiosICANONNewline) { - char input[3] = {'a', 'b', 'c'}; - ASSERT_THAT(WriteFd(master_.get(), input, sizeof(input)), - SyscallSucceedsWithValue(sizeof(input))); - - // Extra bytes for newline (written later) and NUL for EXPECT_STREQ. - char buf[5] = {}; - - // Nothing available yet. - ASSERT_THAT(PollAndReadFd(replica_.get(), buf, sizeof(input), kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); - - char delim = '\n'; - ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); - - // Now it is available. - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), sizeof(input) + 1)); - ExpectReadable(replica_, sizeof(input) + 1, buf); - EXPECT_STREQ(buf, "abc\n"); - - ExpectFinished(replica_); -} - -// ICANON doesn't make input available until a line delimiter is typed. -// -// Test EOF (^D). -TEST_F(PtyTest, TermiosICANONEOF) { - char input[3] = {'a', 'b', 'c'}; - ASSERT_THAT(WriteFd(master_.get(), input, sizeof(input)), - SyscallSucceedsWithValue(sizeof(input))); - - // Extra byte for NUL for EXPECT_STREQ. - char buf[4] = {}; - - // Nothing available yet. - ASSERT_THAT(PollAndReadFd(replica_.get(), buf, sizeof(input), kTimeout), - PosixErrorIs(ETIMEDOUT, ::testing::StrEq("Poll timed out"))); - char delim = ControlCharacter('D'); - ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); - - // Now it is available. Note that ^D is not included. - ExpectReadable(replica_, sizeof(input), buf); - EXPECT_STREQ(buf, "abc"); - - ExpectFinished(replica_); -} - -// ICANON limits us to 4096 bytes including a terminating character. Anything -// after and 4095th character is discarded (although still processed for -// signals and echoing). -TEST_F(PtyTest, CanonDiscard) { - constexpr char kInput = 'M'; - constexpr int kInputSize = 4100; - constexpr int kIter = 3; - - // A few times write more than the 4096 character maximum, then a newline. - constexpr char delim = '\n'; - for (int i = 0; i < kIter; i++) { - // This makes too many syscalls for save/restore. - const DisableSave ds; - for (int i = 0; i < kInputSize; i++) { - ASSERT_THAT(WriteFd(master_.get(), &kInput, sizeof(kInput)), - SyscallSucceedsWithValue(sizeof(kInput))); - } - ASSERT_THAT(WriteFd(master_.get(), &delim, 1), SyscallSucceedsWithValue(1)); - } - - // There should be multiple truncated lines available to read. - for (int i = 0; i < kIter; i++) { - char buf[kInputSize] = {}; - ExpectReadable(replica_, kMaxLineSize, buf); - EXPECT_EQ(buf[kMaxLineSize - 1], delim); - EXPECT_EQ(buf[kMaxLineSize - 2], kInput); - } - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, CanonMultiline) { - constexpr char kInput1[] = "GO\n"; - constexpr char kInput2[] = "BLUE\n"; - - // Write both lines. - ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1), - SyscallSucceedsWithValue(sizeof(kInput1) - 1)); - ASSERT_THAT(WriteFd(master_.get(), kInput2, sizeof(kInput2) - 1), - SyscallSucceedsWithValue(sizeof(kInput2) - 1)); - - // Get the first line. - char line1[8] = {}; - ExpectReadable(replica_, sizeof(kInput1) - 1, line1); - EXPECT_STREQ(line1, kInput1); - - // Get the second line. - char line2[8] = {}; - ExpectReadable(replica_, sizeof(kInput2) - 1, line2); - EXPECT_STREQ(line2, kInput2); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, SwitchNoncanonToCanonMultiline) { - DisableCanonical(); - - constexpr char kInput1[] = "GO\n"; - constexpr char kInput2[] = "BLUE\n"; - constexpr char kExpected[] = "GO\nBLUE\n"; - - // Write both lines. - ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1), - SyscallSucceedsWithValue(sizeof(kInput1) - 1)); - ASSERT_THAT(WriteFd(master_.get(), kInput2, sizeof(kInput2) - 1), - SyscallSucceedsWithValue(sizeof(kInput2) - 1)); - - ASSERT_NO_ERRNO( - WaitUntilReceived(replica_.get(), sizeof(kInput1) + sizeof(kInput2) - 2)); - EnableCanonical(); - - // Get all together as one line. - char line[9] = {}; - ExpectReadable(replica_, 8, line); - EXPECT_STREQ(line, kExpected); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, SwitchTwiceMultiline) { - std::string kInputs[] = {"GO\n", "BLUE\n", "!"}; - std::string kExpected = "GO\nBLUE\n!"; - - // Write each line. - for (const std::string& input : kInputs) { - ASSERT_THAT(WriteFd(master_.get(), input.c_str(), input.size()), - SyscallSucceedsWithValue(input.size())); - } - - DisableCanonical(); - // All written characters have to make it into the input queue before - // canonical mode is re-enabled. If the final '!' character hasn't been - // enqueued before canonical mode is re-enabled, it won't be readable. - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), kExpected.size())); - EnableCanonical(); - - // Get all together as one line. - char line[10] = {}; - ExpectReadable(replica_, 9, line); - EXPECT_STREQ(line, kExpected.c_str()); - - ExpectFinished(replica_); -} - -TEST_F(PtyTest, QueueSize) { - // Write the line. - constexpr char kInput1[] = "GO\n"; - ASSERT_THAT(WriteFd(master_.get(), kInput1, sizeof(kInput1) - 1), - SyscallSucceedsWithValue(sizeof(kInput1) - 1)); - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), sizeof(kInput1) - 1)); - - // Ensure that writing more (beyond what is readable) does not impact the - // readable size. - char input[kMaxLineSize]; - memset(input, 'M', kMaxLineSize); - ASSERT_THAT(WriteFd(master_.get(), input, kMaxLineSize), - SyscallSucceedsWithValue(kMaxLineSize)); - int inputBufSize = ASSERT_NO_ERRNO_AND_VALUE( - WaitUntilReceived(replica_.get(), sizeof(kInput1) - 1)); - EXPECT_EQ(inputBufSize, sizeof(kInput1) - 1); -} - -TEST_F(PtyTest, PartialBadBuffer) { - // Allocate 2 pages. - void* addr = mmap(nullptr, 2 * kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(addr, MAP_FAILED); - char* buf = reinterpret_cast<char*>(addr); - - // Guard the 2nd page for our read to run into. - ASSERT_THAT( - mprotect(reinterpret_cast<void*>(buf + kPageSize), kPageSize, PROT_NONE), - SyscallSucceeds()); - - // Leave only one free byte in the buffer. - char* bad_buffer = buf + kPageSize - 1; - - // Write to the master. - constexpr char kBuf[] = "hello\n"; - constexpr size_t size = sizeof(kBuf) - 1; - EXPECT_THAT(WriteFd(master_.get(), kBuf, size), - SyscallSucceedsWithValue(size)); - - // Read from the replica into bad_buffer. - ASSERT_NO_ERRNO(WaitUntilReceived(replica_.get(), size)); - // Before Linux 3b830a9c this returned EFAULT, but after that commit it - // returns EAGAIN. - EXPECT_THAT( - ReadFd(replica_.get(), bad_buffer, size), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EAGAIN))); - - EXPECT_THAT(munmap(addr, 2 * kPageSize), SyscallSucceeds()) << addr; -} - -TEST_F(PtyTest, SimpleEcho) { - constexpr char kInput[] = "Mr. Eko"; - EXPECT_THAT(WriteFd(master_.get(), kInput, strlen(kInput)), - SyscallSucceedsWithValue(strlen(kInput))); - - char buf[100] = {}; - ExpectReadable(master_, strlen(kInput), buf); - - EXPECT_STREQ(buf, kInput); - ExpectFinished(master_); -} - -TEST_F(PtyTest, GetWindowSize) { - struct winsize ws; - ASSERT_THAT(ioctl(replica_.get(), TIOCGWINSZ, &ws), SyscallSucceeds()); - EXPECT_EQ(ws.ws_row, 0); - EXPECT_EQ(ws.ws_col, 0); -} - -TEST_F(PtyTest, SetReplicaWindowSize) { - constexpr uint16_t kRows = 343; - constexpr uint16_t kCols = 2401; - struct winsize ws = {.ws_row = kRows, .ws_col = kCols}; - ASSERT_THAT(ioctl(replica_.get(), TIOCSWINSZ, &ws), SyscallSucceeds()); - - struct winsize retrieved_ws = {}; - ASSERT_THAT(ioctl(master_.get(), TIOCGWINSZ, &retrieved_ws), - SyscallSucceeds()); - EXPECT_EQ(retrieved_ws.ws_row, kRows); - EXPECT_EQ(retrieved_ws.ws_col, kCols); -} - -TEST_F(PtyTest, SetMasterWindowSize) { - constexpr uint16_t kRows = 343; - constexpr uint16_t kCols = 2401; - struct winsize ws = {.ws_row = kRows, .ws_col = kCols}; - ASSERT_THAT(ioctl(master_.get(), TIOCSWINSZ, &ws), SyscallSucceeds()); - - struct winsize retrieved_ws = {}; - ASSERT_THAT(ioctl(replica_.get(), TIOCGWINSZ, &retrieved_ws), - SyscallSucceeds()); - EXPECT_EQ(retrieved_ws.ws_row, kRows); - EXPECT_EQ(retrieved_ws.ws_col, kCols); -} - -class JobControlTest : public ::testing::Test { - protected: - void SetUp() override { - master_ = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK)); - replica_ = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master_)); - - // Make this a session leader, which also drops the controlling terminal. - // In the gVisor test environment, this test will be run as the session - // leader already (as the sentry init process). - if (!IsRunningOnGvisor()) { - // Ignore failure because setsid(2) fails if the process is already the - // session leader. - setsid(); - ioctl(replica_.get(), TIOCNOTTY); - } - } - - PosixError RunInChild(SubprocessCallback childFunc) { - pid_t child = fork(); - if (!child) { - childFunc(); - _exit(0); - } - int wstatus; - if (waitpid(child, &wstatus, 0) != child) { - return PosixError( - errno, absl::StrCat("child failed with wait status: ", wstatus)); - } - return PosixError(wstatus, "process returned"); - } - - // Master and replica ends of the PTY. Non-blocking. - FileDescriptor master_; - FileDescriptor replica_; -}; - -TEST_F(JobControlTest, SetTTYMaster) { - auto res = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(master_.get(), TIOCSCTTY, 0)); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, SetTTY) { - auto res = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(ioctl(!replica_.get(), TIOCSCTTY, 0)); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, SetTTYNonLeader) { - // Fork a process that won't be the session leader. - auto res = - RunInChild([=]() { TEST_PCHECK(ioctl(replica_.get(), TIOCSCTTY, 0)); }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, SetTTYBadArg) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - auto res = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 1)); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, SetTTYDifferentSession) { - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - auto res = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 1)); - - // Fork, join a new session, and try to steal the parent's controlling - // terminal, which should fail. - pid_t grandchild = fork(); - if (!grandchild) { - TEST_PCHECK(setsid() >= 0); - // We shouldn't be able to steal the terminal. - TEST_PCHECK(ioctl(replica_.get(), TIOCSCTTY, 1)); - _exit(0); - } - - int gcwstatus; - TEST_PCHECK(waitpid(grandchild, &gcwstatus, 0) == grandchild); - TEST_PCHECK(gcwstatus == 0); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, ReleaseTTY) { - ASSERT_THAT(ioctl(replica_.get(), TIOCSCTTY, 0), SyscallSucceeds()); - - // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - struct sigaction old_sa; - EXPECT_THAT(sigaction(SIGHUP, &sa, &old_sa), SyscallSucceeds()); - EXPECT_THAT(ioctl(replica_.get(), TIOCNOTTY), SyscallSucceeds()); - EXPECT_THAT(sigaction(SIGHUP, &old_sa, NULL), SyscallSucceeds()); -} - -TEST_F(JobControlTest, ReleaseUnsetTTY) { - ASSERT_THAT(ioctl(replica_.get(), TIOCNOTTY), SyscallFailsWithErrno(ENOTTY)); -} - -TEST_F(JobControlTest, ReleaseWrongTTY) { - auto res = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - TEST_PCHECK(ioctl(master_.get(), TIOCNOTTY) < 0 && errno == ENOTTY); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, ReleaseTTYNonLeader) { - auto ret = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - - pid_t grandchild = fork(); - if (!grandchild) { - TEST_PCHECK(!ioctl(replica_.get(), TIOCNOTTY)); - _exit(0); - } - - int wstatus; - TEST_PCHECK(waitpid(grandchild, &wstatus, 0) == grandchild); - TEST_PCHECK(wstatus == 0); - }); - ASSERT_NO_ERRNO(ret); -} - -TEST_F(JobControlTest, ReleaseTTYDifferentSession) { - auto ret = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - - pid_t grandchild = fork(); - if (!grandchild) { - // Join a new session, then try to disconnect. - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(ioctl(replica_.get(), TIOCNOTTY)); - _exit(0); - } - - int wstatus; - TEST_PCHECK(waitpid(grandchild, &wstatus, 0) == grandchild); - TEST_PCHECK(wstatus == 0); - }); - ASSERT_NO_ERRNO(ret); -} - -// Used by the child process spawned in ReleaseTTYSignals to track received -// signals. -static int received; - -void sig_handler(int signum) { received |= signum; } - -// When the session leader releases its controlling terminal, the foreground -// process group gets SIGHUP, then SIGCONT. This test: -// - Spawns 2 threads -// - Has thread 1 return 0 if it gets both SIGHUP and SIGCONT -// - Has thread 2 leave the foreground process group, and return non-zero if it -// receives any signals. -// - Has the parent thread release its controlling terminal -// - Checks that thread 1 got both signals -// - Checks that thread 2 didn't get any signals. -TEST_F(JobControlTest, ReleaseTTYSignals) { - ASSERT_THAT(ioctl(replica_.get(), TIOCSCTTY, 0), SyscallSucceeds()); - - received = 0; - struct sigaction sa = {}; - sa.sa_handler = sig_handler; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaddset(&sa.sa_mask, SIGHUP); - sigaddset(&sa.sa_mask, SIGCONT); - sigprocmask(SIG_BLOCK, &sa.sa_mask, NULL); - - pid_t same_pgrp_child = fork(); - if (!same_pgrp_child) { - // The child will wait for SIGHUP and SIGCONT, then return 0. It begins with - // SIGHUP and SIGCONT blocked. We install signal handlers for those signals, - // then use sigsuspend to wait for those specific signals. - TEST_PCHECK(!sigaction(SIGHUP, &sa, NULL)); - TEST_PCHECK(!sigaction(SIGCONT, &sa, NULL)); - sigset_t mask; - sigfillset(&mask); - sigdelset(&mask, SIGHUP); - sigdelset(&mask, SIGCONT); - while (received != (SIGHUP | SIGCONT)) { - sigsuspend(&mask); - } - _exit(0); - } - - // We don't want to block these anymore. - sigprocmask(SIG_UNBLOCK, &sa.sa_mask, NULL); - - // This child will return non-zero if either SIGHUP or SIGCONT are received. - pid_t diff_pgrp_child = fork(); - if (!diff_pgrp_child) { - TEST_PCHECK(!setpgid(0, 0)); - TEST_PCHECK(pause()); - _exit(1); - } - - EXPECT_THAT(setpgid(diff_pgrp_child, diff_pgrp_child), SyscallSucceeds()); - - // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. - struct sigaction sighup_sa = {}; - sighup_sa.sa_handler = SIG_IGN; - sighup_sa.sa_flags = 0; - sigemptyset(&sighup_sa.sa_mask); - struct sigaction old_sa; - EXPECT_THAT(sigaction(SIGHUP, &sighup_sa, &old_sa), SyscallSucceeds()); - - // Release the controlling terminal, sending SIGHUP and SIGCONT to all other - // processes in this process group. - EXPECT_THAT(ioctl(replica_.get(), TIOCNOTTY), SyscallSucceeds()); - - EXPECT_THAT(sigaction(SIGHUP, &old_sa, NULL), SyscallSucceeds()); - - // The child in the same process group will get signaled. - int wstatus; - EXPECT_THAT(waitpid(same_pgrp_child, &wstatus, 0), - SyscallSucceedsWithValue(same_pgrp_child)); - EXPECT_EQ(wstatus, 0); - - // The other child will not get signaled. - EXPECT_THAT(waitpid(diff_pgrp_child, &wstatus, WNOHANG), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(kill(diff_pgrp_child, SIGKILL), SyscallSucceeds()); -} - -TEST_F(JobControlTest, GetForegroundProcessGroup) { - auto res = RunInChild([=]() { - pid_t pid, foreground_pgid; - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 1)); - TEST_PCHECK(!ioctl(replica_.get(), TIOCGPGRP, &foreground_pgid)); - TEST_PCHECK((pid = getpid()) >= 0); - TEST_PCHECK(pid == foreground_pgid); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, GetForegroundProcessGroupNonControlling) { - // At this point there's no controlling terminal, so TIOCGPGRP should fail. - pid_t foreground_pgid; - ASSERT_THAT(ioctl(replica_.get(), TIOCGPGRP, &foreground_pgid), - SyscallFailsWithErrno(ENOTTY)); -} - -// This test: -// - sets itself as the foreground process group -// - creates a child process in a new process group -// - sets that child as the foreground process group -// - kills its child and sets itself as the foreground process group. -// TODO(gvisor.dev/issue/5357): Fix and enable. -TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroup) { - auto res = RunInChild([=]() { - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - - // Ignore SIGTTOU so that we don't stop ourself when calling tcsetpgrp. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sigaction(SIGTTOU, &sa, NULL); - - // Set ourself as the foreground process group. - TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0))); - - // Create a new process that just waits to be signaled. - pid_t grandchild = fork(); - if (!grandchild) { - TEST_PCHECK(!pause()); - // We should never reach this. - _exit(1); - } - - // Make the child its own process group, then make it the controlling - // process group of the terminal. - TEST_PCHECK(!setpgid(grandchild, grandchild)); - TEST_PCHECK(!tcsetpgrp(replica_.get(), grandchild)); - - // Sanity check - we're still the controlling session. - TEST_PCHECK(getsid(0) == getsid(grandchild)); - - // Signal the child, wait for it to exit, then retake the terminal. - TEST_PCHECK(!kill(grandchild, SIGTERM)); - int wstatus; - TEST_PCHECK(waitpid(grandchild, &wstatus, 0) == grandchild); - TEST_PCHECK(WIFSIGNALED(wstatus)); - TEST_PCHECK(WTERMSIG(wstatus) == SIGTERM); - - // Set ourself as the foreground process. - pid_t pgid; - TEST_PCHECK(pgid = getpgid(0) == 0); - TEST_PCHECK(!tcsetpgrp(replica_.get(), pgid)); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, SetForegroundProcessGroupWrongTTY) { - pid_t pid = getpid(); - ASSERT_THAT(ioctl(replica_.get(), TIOCSPGRP, &pid), - SyscallFailsWithErrno(ENOTTY)); -} - -TEST_F(JobControlTest, SetForegroundProcessGroupNegPgid) { - auto ret = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - - pid_t pid = -1; - TEST_PCHECK(ioctl(replica_.get(), TIOCSPGRP, &pid) && errno == EINVAL); - }); - ASSERT_NO_ERRNO(ret); -} - -// TODO(gvisor.dev/issue/5357): Fix and enable. -TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroupEmptyProcessGroup) { - auto res = RunInChild([=]() { - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - - // Create a new process, put it in a new process group, make that group the - // foreground process group, then have the process wait. - pid_t grandchild = fork(); - if (!grandchild) { - TEST_PCHECK(!setpgid(0, 0)); - _exit(0); - } - - // Wait for the child to exit. - int wstatus; - TEST_PCHECK(waitpid(grandchild, &wstatus, 0) == grandchild); - // The child's process group doesn't exist anymore - this should fail. - TEST_PCHECK(ioctl(replica_.get(), TIOCSPGRP, &grandchild) != 0 && - errno == ESRCH); - }); - ASSERT_NO_ERRNO(res); -} - -TEST_F(JobControlTest, SetForegroundProcessGroupDifferentSession) { - auto ret = RunInChild([=]() { - TEST_PCHECK(setsid() >= 0); - TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0)); - - int sync_setsid[2]; - int sync_exit[2]; - TEST_PCHECK(pipe(sync_setsid) >= 0); - TEST_PCHECK(pipe(sync_exit) >= 0); - - // Create a new process and put it in a new session. - pid_t grandchild = fork(); - if (!grandchild) { - TEST_PCHECK(setsid() >= 0); - // Tell the parent we're in a new session. - char c = 'c'; - TEST_PCHECK(WriteFd(sync_setsid[1], &c, 1) == 1); - TEST_PCHECK(ReadFd(sync_exit[0], &c, 1) == 1); - _exit(0); - } - - // Wait for the child to tell us it's in a new session. - char c = 'c'; - TEST_PCHECK(ReadFd(sync_setsid[0], &c, 1) == 1); - - // Child is in a new session, so we can't make it the foregroup process - // group. - TEST_PCHECK(ioctl(replica_.get(), TIOCSPGRP, &grandchild) && - errno == EPERM); - - TEST_PCHECK(WriteFd(sync_exit[1], &c, 1) == 1); - - int wstatus; - TEST_PCHECK(waitpid(grandchild, &wstatus, 0) == grandchild); - TEST_PCHECK(WIFEXITED(wstatus)); - TEST_PCHECK(!WEXITSTATUS(wstatus)); - }); - ASSERT_NO_ERRNO(ret); -} - -// Verify that we don't hang when creating a new session from an orphaned -// process group (b/139968068). Calling setsid() creates an orphaned process -// group, as process groups that contain the session's leading process are -// orphans. -// -// We create 2 sessions in this test. The init process in gVisor is considered -// not to be an orphan (see sessions.go), so we have to create a session from -// which to create a session. The latter session is being created from an -// orphaned process group. -TEST_F(JobControlTest, OrphanRegression) { - pid_t session_2_leader = fork(); - if (!session_2_leader) { - TEST_PCHECK(setsid() >= 0); - - pid_t session_3_leader = fork(); - if (!session_3_leader) { - TEST_PCHECK(setsid() >= 0); - - _exit(0); - } - - int wstatus; - TEST_PCHECK(waitpid(session_3_leader, &wstatus, 0) == session_3_leader); - TEST_PCHECK(wstatus == 0); - - _exit(0); - } - - int wstatus; - ASSERT_THAT(waitpid(session_2_leader, &wstatus, 0), - SyscallSucceedsWithValue(session_2_leader)); - ASSERT_EQ(wstatus, 0); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/pty_root.cc b/test/syscalls/linux/pty_root.cc deleted file mode 100644 index 4ac648729..000000000 --- a/test/syscalls/linux/pty_root.cc +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/ioctl.h> -#include <termios.h> - -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/pty_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// StealTTY tests whether privileged processes can steal controlling terminals. -// If the stealing process has CAP_SYS_ADMIN in the root user namespace, the -// test ensures that stealing works. If it has non-root CAP_SYS_ADMIN, it -// ensures stealing fails. -TEST(JobControlRootTest, StealTTY) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - bool true_root = true; - if (!IsRunningOnGvisor()) { - // If running in Linux, we may only have CAP_SYS_ADMIN in a non-root user - // namespace (i.e. we are not truly root). We use init_module as a proxy for - // whether we are true root, as it returns EPERM immediately. - ASSERT_THAT(syscall(SYS_init_module, nullptr, 0, nullptr), SyscallFails()); - true_root = errno != EPERM; - - // Make this a session leader, which also drops the controlling terminal. - // In the gVisor test environment, this test will be run as the session - // leader already (as the sentry init process). - ASSERT_THAT(setsid(), SyscallSucceeds()); - } - - FileDescriptor master = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/ptmx", O_RDWR | O_NONBLOCK)); - FileDescriptor replica = ASSERT_NO_ERRNO_AND_VALUE(OpenReplica(master)); - - // Make replica the controlling terminal. - ASSERT_THAT(ioctl(replica.get(), TIOCSCTTY, 0), SyscallSucceeds()); - - // Fork, join a new session, and try to steal the parent's controlling - // terminal, which should succeed when we have CAP_SYS_ADMIN and pass an arg - // of 1. - pid_t child = fork(); - if (!child) { - ASSERT_THAT(setsid(), SyscallSucceeds()); - // We shouldn't be able to steal the terminal with the wrong arg value. - TEST_PCHECK(ioctl(replica.get(), TIOCSCTTY, 0)); - // We should be able to steal it if we are true root. - TEST_PCHECK(true_root == !ioctl(replica.get(), TIOCSCTTY, 1)); - _exit(0); - } - - int wstatus; - ASSERT_THAT(waitpid(child, &wstatus, 0), SyscallSucceedsWithValue(child)); - ASSERT_EQ(wstatus, 0); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/pwrite64.cc b/test/syscalls/linux/pwrite64.cc deleted file mode 100644 index 1b2f25363..000000000 --- a/test/syscalls/linux/pwrite64.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <linux/unistd.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// TODO(gvisor.dev/issue/2370): This test is currently very rudimentary. -class Pwrite64 : public ::testing::Test { - void SetUp() override { - name_ = NewTempAbsPath(); - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_CREAT, 0644), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - } - - void TearDown() override { unlink(name_.c_str()); } - - public: - std::string name_; -}; - -TEST_F(Pwrite64, AppendOnly) { - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_APPEND | O_RDWR), SyscallSucceeds()); - constexpr int64_t kBufSize = 1024; - std::vector<char> buf(kBufSize); - std::fill(buf.begin(), buf.end(), 'a'); - EXPECT_THAT(PwriteFd(fd, buf.data(), buf.size(), 0), - SyscallSucceedsWithValue(buf.size())); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(Pwrite64, InvalidArgs) { - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_APPEND | O_RDWR), SyscallSucceeds()); - constexpr int64_t kBufSize = 1024; - std::vector<char> buf(kBufSize); - std::fill(buf.begin(), buf.end(), 'a'); - EXPECT_THAT(PwriteFd(fd, buf.data(), buf.size(), -1), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(Pwrite64, Overflow) { - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_APPEND | O_RDWR), SyscallSucceeds()); - constexpr int64_t kBufSize = 1024; - std::vector<char> buf(kBufSize); - std::fill(buf.begin(), buf.end(), 'a'); - EXPECT_THAT(PwriteFd(fd, buf.data(), buf.size(), 0x7fffffffffffffffull), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(Pwrite64, Pwrite64WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - std::vector<char> buf(1); - EXPECT_THAT(PwriteFd(fd.get(), buf.data(), 1, 0), - SyscallFailsWithErrno(EBADF)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/pwritev2.cc b/test/syscalls/linux/pwritev2.cc deleted file mode 100644 index 00aed61b4..000000000 --- a/test/syscalls/linux/pwritev2.cc +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/uio.h> - -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/file_base.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#ifndef SYS_pwritev2 -#if defined(__x86_64__) -#define SYS_pwritev2 328 -#elif defined(__aarch64__) -#define SYS_pwritev2 287 -#else -#error "Unknown architecture" -#endif -#endif // SYS_pwrite2 - -#ifndef RWF_HIPRI -#define RWF_HIPRI 0x1 -#endif // RWF_HIPRI - -#ifndef RWF_DSYNC -#define RWF_DSYNC 0x2 -#endif // RWF_DSYNC - -#ifndef RWF_SYNC -#define RWF_SYNC 0x4 -#endif // RWF_SYNC - -constexpr int kBufSize = 1024; - -void SetContent(std::vector<char>& content) { - for (uint i = 0; i < content.size(); i++) { - content[i] = static_cast<char>((i % 10) + '0'); - } -} - -ssize_t pwritev2(unsigned long fd, const struct iovec* iov, - unsigned long iovcnt, off_t offset, unsigned long flags) { - // syscall on pwritev2 does some weird things (see man syscall and search - // pwritev2), so we insert a 0 to word align the flags argument on native. - return syscall(SYS_pwritev2, fd, iov, iovcnt, offset, 0, flags); -} - -// This test is the base case where we call pwritev (no offset, no flags). -TEST(Writev2Test, BaseCall) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - std::vector<char> content(kBufSize); - SetContent(content); - struct iovec iov[2]; - iov[0].iov_base = content.data(); - iov[0].iov_len = content.size() / 2; - iov[1].iov_base = static_cast<char*>(iov[0].iov_base) + (content.size() / 2); - iov[1].iov_len = content.size() / 2; - - ASSERT_THAT(pwritev2(fd.get(), iov, /*iovcnt=*/2, - /*offset=*/0, /*flags=*/0), - SyscallSucceedsWithValue(kBufSize)); - - std::vector<char> buf(kBufSize); - EXPECT_THAT(read(fd.get(), buf.data(), kBufSize), - SyscallSucceedsWithValue(kBufSize)); - - EXPECT_EQ(content, buf); -} - -// This test is where we call pwritev2 with a positive offset and no flags. -TEST(Pwritev2Test, ValidPositiveOffset) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - std::string prefix(kBufSize, '0'); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), prefix, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - std::vector<char> content(kBufSize); - SetContent(content); - struct iovec iov; - iov.iov_base = content.data(); - iov.iov_len = content.size(); - - ASSERT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, - /*offset=*/prefix.size(), /*flags=*/0), - SyscallSucceedsWithValue(content.size())); - - std::vector<char> buf(prefix.size() + content.size()); - EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - std::vector<char> want(prefix.begin(), prefix.end()); - want.insert(want.end(), content.begin(), content.end()); - EXPECT_EQ(want, buf); -} - -// This test is the base case where we call writev by using -1 as the offset. -// The write should use the file offset, so the test increments the file offset -// prior to call pwritev2. -TEST(Pwritev2Test, NegativeOneOffset) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const std::string prefix = "00"; - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), prefix.data(), TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - ASSERT_THAT(lseek(fd.get(), prefix.size(), SEEK_SET), - SyscallSucceedsWithValue(prefix.size())); - - std::vector<char> content(kBufSize); - SetContent(content); - struct iovec iov; - iov.iov_base = content.data(); - iov.iov_len = content.size(); - - ASSERT_THAT(pwritev2(fd.get(), &iov, /*iovcnt*/ 1, - /*offset=*/static_cast<off_t>(-1), /*flags=*/0), - SyscallSucceedsWithValue(content.size())); - - ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(prefix.size() + content.size())); - - std::vector<char> buf(prefix.size() + content.size()); - EXPECT_THAT(pread(fd.get(), buf.data(), buf.size(), /*offset=*/0), - SyscallSucceedsWithValue(buf.size())); - - std::vector<char> want(prefix.begin(), prefix.end()); - want.insert(want.end(), content.begin(), content.end()); - EXPECT_EQ(want, buf); -} - -// pwritev2 requires if the RWF_HIPRI flag is passed, the fd must be opened with -// O_DIRECT. This test implements a correct call with the RWF_HIPRI flag. -TEST(Pwritev2Test, CallWithRWF_HIPRI) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - std::vector<char> content(kBufSize); - SetContent(content); - struct iovec iov; - iov.iov_base = content.data(); - iov.iov_len = content.size(); - - EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, - /*offset=*/0, /*flags=*/RWF_HIPRI), - SyscallSucceedsWithValue(kBufSize)); - - std::vector<char> buf(content.size()); - EXPECT_THAT(read(fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - EXPECT_EQ(buf, content); -} - -// This test calls pwritev2 with a bad file descriptor. -TEST(Writev2Test, BadFile) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - ASSERT_THAT(pwritev2(/*fd=*/-1, /*iov=*/nullptr, /*iovcnt=*/0, - /*offset=*/0, /*flags=*/0), - SyscallFailsWithErrno(EBADF)); -} - -// This test calls pwrite2 with an invalid offset. -TEST(Pwritev2Test, InvalidOffset) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - char buf[16]; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - - EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, - /*offset=*/static_cast<off_t>(-8), /*flags=*/0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(Pwritev2Test, UnseekableFileValid) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - int pipe_fds[2]; - - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - - std::vector<char> content(32, '0'); - SetContent(content); - struct iovec iov; - iov.iov_base = content.data(); - iov.iov_len = content.size(); - - EXPECT_THAT(pwritev2(pipe_fds[1], &iov, /*iovcnt=*/1, - /*offset=*/static_cast<off_t>(-1), /*flags=*/0), - SyscallSucceedsWithValue(content.size())); - - std::vector<char> buf(content.size()); - EXPECT_THAT(read(pipe_fds[0], buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - EXPECT_EQ(content, buf); - - EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); -} - -// Calling pwritev2 with a non-negative offset calls pwritev. Calling pwritev -// with an unseekable file is not allowed. A pipe is used for an unseekable -// file. -TEST(Pwritev2Test, UnseekableFileInvalid) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - int pipe_fds[2]; - char buf[16]; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - - EXPECT_THAT(pwritev2(pipe_fds[1], &iov, /*iovcnt=*/1, - /*offset=*/2, /*flags=*/0), - SyscallFailsWithErrno(ESPIPE)); - - EXPECT_THAT(close(pipe_fds[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipe_fds[1]), SyscallSucceeds()); -} - -TEST(Pwritev2Test, ReadOnlyFile) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - char buf[16]; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - - EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, - /*offset=*/0, /*flags=*/0), - SyscallFailsWithErrno(EBADF)); -} - -TEST(Pwritev2Test, Pwritev2WithOpath) { - SKIP_IF(IsRunningWithVFS1()); - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - char buf[16]; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - - EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, /*offset=*/0, /*flags=*/0), - SyscallFailsWithErrno(EBADF)); -} - -// This test calls pwritev2 with an invalid flag. -TEST(Pwritev2Test, InvalidFlag) { - SKIP_IF(pwritev2(-1, nullptr, 0, 0, 0) < 0 && errno == ENOSYS); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR | O_DIRECT)); - - char buf[16]; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - - EXPECT_THAT(pwritev2(fd.get(), &iov, /*iovcnt=*/1, - /*offset=*/0, /*flags=*/0xF0), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc deleted file mode 100644 index 32924466f..000000000 --- a/test/syscalls/linux/raw_socket.cc +++ /dev/null @@ -1,902 +0,0 @@ -// Copyright 2019 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 <linux/capability.h> -#include <linux/filter.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/ip6.h> -#include <netinet/ip_icmp.h> -#include <poll.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> - -#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/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Note: in order to run these tests, /proc/sys/net/ipv4/ping_group_range will -// need to be configured to let the superuser create ping sockets (see icmp(7)). - -namespace gvisor { -namespace testing { - -namespace { - -// Fixture for tests parameterized by protocol. -class RawSocketTest : public ::testing::TestWithParam<std::tuple<int, int>> { - protected: - // Creates a socket to be used in tests. - void SetUp() override; - - // Closes the socket created by SetUp(). - void TearDown() override; - - // Sends buf via s_. - void SendBuf(const char* buf, int buf_len); - - // Reads from s_ into recv_buf. - void ReceiveBuf(char* recv_buf, size_t recv_buf_len); - - void ReceiveBufFrom(int sock, char* recv_buf, size_t recv_buf_len); - - int Protocol() { return std::get<0>(GetParam()); } - - int Family() { return std::get<1>(GetParam()); } - - socklen_t AddrLen() { - if (Family() == AF_INET) { - return sizeof(sockaddr_in); - } - return sizeof(sockaddr_in6); - } - - int HdrLen() { - if (Family() == AF_INET) { - return sizeof(struct iphdr); - } - // IPv6 raw sockets don't include the header. - return 0; - } - - // The socket used for both reading and writing. - int s_; - - // The loopback address. - struct sockaddr_storage addr_; -}; - -void RawSocketTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - ASSERT_THAT(socket(Family(), SOCK_RAW, Protocol()), - SyscallFailsWithErrno(EPERM)); - GTEST_SKIP(); - } - - ASSERT_THAT(s_ = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds()); - - addr_ = {}; - - // We don't set ports because raw sockets don't have a notion of ports. - if (Family() == AF_INET) { - struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&addr_); - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - } else { - struct sockaddr_in6* sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr_); - sin6->sin6_family = AF_INET6; - sin6->sin6_addr = in6addr_loopback; - } -} - -void RawSocketTest::TearDown() { - // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - EXPECT_THAT(close(s_), SyscallSucceeds()); - } -} - -// We should be able to create multiple raw sockets for the same protocol. -// BasicRawSocket::Setup creates the first one, so we only have to create one -// more here. -TEST_P(RawSocketTest, MultipleCreation) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int s2; - ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds()); - - ASSERT_THAT(close(s2), SyscallSucceeds()); -} - -// Test that shutting down an unconnected socket fails. -TEST_P(RawSocketTest, FailShutdownWithoutConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); - ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); -} - -// Shutdown is a no-op for raw sockets (and datagram sockets in general). -TEST_P(RawSocketTest, ShutdownWriteNoop) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallSucceeds()); - - // Arbitrary. - constexpr char kBuf[] = "noop"; - ASSERT_THAT(RetryEINTR(write)(s_, kBuf, sizeof(kBuf)), - SyscallSucceedsWithValue(sizeof(kBuf))); -} - -// Shutdown is a no-op for raw sockets (and datagram sockets in general). -TEST_P(RawSocketTest, ShutdownReadNoop) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallSucceeds()); - - // Arbitrary. - constexpr char kBuf[] = "gdg"; - ASSERT_NO_FATAL_FAILURE(SendBuf(kBuf, sizeof(kBuf))); - - std::vector<char> c(sizeof(kBuf) + HdrLen()); - ASSERT_THAT(read(s_, c.data(), c.size()), SyscallSucceedsWithValue(c.size())); -} - -// Test that listen() fails. -TEST_P(RawSocketTest, FailListen) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT(listen(s_, 1), SyscallFailsWithErrno(ENOTSUP)); -} - -// Test that accept() fails. -TEST_P(RawSocketTest, FailAccept) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - struct sockaddr saddr; - socklen_t addrlen; - ASSERT_THAT(accept(s_, &saddr, &addrlen), SyscallFailsWithErrno(ENOTSUP)); -} - -// Test that getpeername() returns nothing before connect(). -TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - struct sockaddr saddr; - socklen_t addrlen = sizeof(saddr); - ASSERT_THAT(getpeername(s_, &saddr, &addrlen), - SyscallFailsWithErrno(ENOTCONN)); -} - -// Test that getpeername() returns something after connect(). -TEST_P(RawSocketTest, GetPeerName) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - struct sockaddr saddr; - socklen_t addrlen = sizeof(saddr); - ASSERT_THAT(getpeername(s_, &saddr, &addrlen), - SyscallFailsWithErrno(ENOTCONN)); - ASSERT_GT(addrlen, 0); -} - -// Test that the socket is writable immediately. -TEST_P(RawSocketTest, PollWritableImmediately) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - struct pollfd pfd = {}; - pfd.fd = s_; - pfd.events = POLLOUT; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 10000), SyscallSucceedsWithValue(1)); -} - -// Test that the socket isn't readable before receiving anything. -TEST_P(RawSocketTest, PollNotReadableInitially) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Try to receive data with MSG_DONTWAIT, which returns immediately if there's - // nothing to be read. - char buf[117]; - ASSERT_THAT(RetryEINTR(recv)(s_, buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Test that the socket becomes readable once something is written to it. -TEST_P(RawSocketTest, PollTriggeredOnWrite) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Write something so that there's data to be read. - // Arbitrary. - constexpr char kBuf[] = "JP5"; - ASSERT_NO_FATAL_FAILURE(SendBuf(kBuf, sizeof(kBuf))); - - struct pollfd pfd = {}; - pfd.fd = s_; - pfd.events = POLLIN; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 10000), SyscallSucceedsWithValue(1)); -} - -// Test that we can connect() to a valid IP (loopback). -TEST_P(RawSocketTest, ConnectToLoopback) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); -} - -// Test that calling send() without connect() fails. -TEST_P(RawSocketTest, SendWithoutConnectFails) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Arbitrary. - constexpr char kBuf[] = "Endgame was good"; - ASSERT_THAT(send(s_, kBuf, sizeof(kBuf), 0), - SyscallFailsWithErrno(EDESTADDRREQ)); -} - -// Wildcard Bind. -TEST_P(RawSocketTest, BindToWildcard) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - struct sockaddr_storage addr; - addr = {}; - - // We don't set ports because raw sockets don't have a notion of ports. - if (Family() == AF_INET) { - struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&addr); - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = htonl(INADDR_ANY); - } else { - struct sockaddr_in6* sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr); - sin6->sin6_family = AF_INET6; - sin6->sin6_addr = in6addr_any; - } - - ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); -} - -// Bind to localhost. -TEST_P(RawSocketTest, BindToLocalhost) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); -} - -// Bind to a different address. -TEST_P(RawSocketTest, BindToInvalid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - struct sockaddr_storage bind_addr = addr_; - if (Family() == AF_INET) { - struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&bind_addr); - sin->sin_addr = {1}; // 1.0.0.0 - An address that we can't bind to. - } else { - struct sockaddr_in6* sin6 = - reinterpret_cast<struct sockaddr_in6*>(&bind_addr); - memset(&sin6->sin6_addr.s6_addr, 0, sizeof(sin6->sin6_addr.s6_addr)); - sin6->sin6_addr.s6_addr[0] = 1; // 1: - An address that we can't bind to. - } - ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), - AddrLen()), SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -// Send and receive an packet. -TEST_P(RawSocketTest, SendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Arbitrary. - constexpr char kBuf[] = "TB12"; - ASSERT_NO_FATAL_FAILURE(SendBuf(kBuf, sizeof(kBuf))); - - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), kBuf, sizeof(kBuf)), 0); -} - -// We should be able to create multiple raw sockets for the same protocol and -// receive the same packet on both. -TEST_P(RawSocketTest, MultipleSocketReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int s2; - ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds()); - - // Arbitrary. - constexpr char kBuf[] = "TB10"; - ASSERT_NO_FATAL_FAILURE(SendBuf(kBuf, sizeof(kBuf))); - - // Receive it on socket 1. - std::vector<char> recv_buf1(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf1.data(), recv_buf1.size())); - - // Receive it on socket 2. - std::vector<char> recv_buf2(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBufFrom(s2, recv_buf2.data(), - recv_buf2.size())); - - EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(), - recv_buf2.data() + HdrLen(), sizeof(kBuf)), - 0); - - ASSERT_THAT(close(s2), SyscallSucceeds()); -} - -// Test that connect sends packets to the right place. -TEST_P(RawSocketTest, SendAndReceiveViaConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - - // Arbitrary. - constexpr char kBuf[] = "JH4"; - ASSERT_THAT(send(s_, kBuf, sizeof(kBuf), 0), - SyscallSucceedsWithValue(sizeof(kBuf))); - - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), kBuf, sizeof(kBuf)), 0); -} - -// Bind to localhost, then send and receive packets. -TEST_P(RawSocketTest, BindSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - - // Arbitrary. - constexpr char kBuf[] = "DR16"; - ASSERT_NO_FATAL_FAILURE(SendBuf(kBuf, sizeof(kBuf))); - - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), kBuf, sizeof(kBuf)), 0); -} - -// Bind and connect to localhost and send/receive packets. -TEST_P(RawSocketTest, BindConnectSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - - // Arbitrary. - constexpr char kBuf[] = "DG88"; - ASSERT_NO_FATAL_FAILURE(SendBuf(kBuf, sizeof(kBuf))); - - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(sizeof(kBuf) + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), kBuf, sizeof(kBuf)), 0); -} - -// Check that setting SO_RCVBUF below min is clamped to the minimum -// receive buffer size. -TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover minimum receive buf size by trying to set it to zero. - // See: - // https://github.com/torvalds/linux/blob/a5dc8300df75e8b8384b4c82225f1e4a0b4d9b55/net/core/sock.c#L820 - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - - // Linux doubles the value so let's use a value that when doubled will still - // be smaller than min. - int below_min = min / 2 - 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &below_min, sizeof(below_min)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - - ASSERT_EQ(min, val); -} - -// Check that setting SO_RCVBUF above max is clamped to the maximum -// receive buffer size. -TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover max buf size by trying to set the largest possible buffer size. - constexpr int kRcvBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &max, &max_len), - SyscallSucceeds()); - - int above_max = max + 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &above_max, sizeof(above_max)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(max, val); -} - -// Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored. -TEST_P(RawSocketTest, SetSocketRecvBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int max = 0; - int min = 0; - { - // Discover max buf size by trying to set a really large buffer size. - constexpr int kRcvBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by trying to set a zero size receive buffer - // size. - // See: - // https://github.com/torvalds/linux/blob/a5dc8300df75e8b8384b4c82225f1e4a0b4d9b55/net/core/sock.c#L820 - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &quarter_sz, sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - - // Linux doubles the value set by SO_SNDBUF/SO_RCVBUF. - // TODO(gvisor.dev/issue/2926): Remove when Netstack matches linux behavior. - if (!IsRunningOnGvisor()) { - quarter_sz *= 2; - } - ASSERT_EQ(quarter_sz, val); -} - -// Check that setting SO_SNDBUF below min is clamped to the minimum -// receive buffer size. -TEST_P(RawSocketTest, SetSocketSendBufBelowMin) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover minimum buffer size by trying to set it to zero. - constexpr int kSndBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - - // Linux doubles the value so let's use a value that when doubled will still - // be smaller than min. - int below_min = min / 2 - 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &below_min, sizeof(below_min)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - ASSERT_EQ(min, val); -} - -// Check that setting SO_SNDBUF above max is clamped to the maximum -// send buffer size. -TEST_P(RawSocketTest, SetSocketSendBufAboveMax) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Discover maximum buffer size by trying to set it to a large value. - constexpr int kSndBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - int max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - - int above_max = max + 1; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &above_max, sizeof(above_max)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(max, val); -} - -// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored. -TEST_P(RawSocketTest, SetSocketSendBuf) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int max = 0; - int min = 0; - { - // Discover maximum buffer size by trying to set it to a large value. - constexpr int kSndBufSz = 0xffffffff; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by trying to set it to zero. - constexpr int kSndBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &kSndBufSz, sizeof(kSndBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_SNDBUF, &quarter_sz, sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - quarter_sz *= 2; - ASSERT_EQ(quarter_sz, val); -} - -// Test that receive buffer limits are not enforced when the recv buffer is -// empty. -TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - - int min = 0; - { - // Discover minimum buffer size by trying to set it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - } - - { - // Send data of size min and verify that it's received. - std::vector<char> buf(min); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(buf.size() + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ( - memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), - 0); - } - - { - // Send data of size min + 1 and verify that its received. Both linux and - // Netstack accept a dgram that exceeds rcvBuf limits if the receive buffer - // is currently empty. - std::vector<char> buf(min + 1); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(buf.size() + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ( - memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), - 0); - } -} - -TEST_P(RawSocketTest, RecvBufLimits) { - // TCP stack generates RSTs for unknown endpoints and it complicates the test - // as we have to deal with the RST packets as well. For testing the raw socket - // endpoints buffer limit enforcement we can just test for UDP. - // - // We don't use SKIP_IF here because root_test_runner explicitly fails if a - // test is skipped. - if (Protocol() == IPPROTO_TCP) { - return; - } - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()), - SyscallSucceeds()); - - int min = 0; - { - // Discover minimum buffer size by trying to set it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - } - - // Now set the limit to min * 2. - int new_rcv_buf_sz = min * 4; - if (!IsRunningOnGvisor()) { - // Linux doubles the value specified so just set to min. - new_rcv_buf_sz = min * 2; - } - - ASSERT_THAT(setsockopt(s_, SOL_SOCKET, SO_RCVBUF, &new_rcv_buf_sz, - sizeof(new_rcv_buf_sz)), - SyscallSucceeds()); - int rcv_buf_sz = 0; - { - socklen_t rcv_buf_len = sizeof(rcv_buf_sz); - ASSERT_THAT( - getsockopt(s_, SOL_SOCKET, SO_RCVBUF, &rcv_buf_sz, &rcv_buf_len), - SyscallSucceeds()); - } - - // Set a receive timeout so that we don't block forever on reads if the test - // fails. - struct timeval tv { - .tv_sec = 1, .tv_usec = 0, - }; - ASSERT_THAT(setsockopt(s_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - { - std::vector<char> buf(min); - RandomizeBuffer(buf.data(), buf.size()); - - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - int sent = 4; - if (IsRunningOnGvisor()) { - // Linux seems to drop the 4th packet even though technically it should - // fit in the receive buffer. - ASSERT_NO_FATAL_FAILURE(SendBuf(buf.data(), buf.size())); - sent++; - } - - // Verify that the expected number of packets are available to be read. - for (int i = 0; i < sent - 1; i++) { - // Receive the packet and make sure it's identical. - std::vector<char> recv_buf(buf.size() + HdrLen()); - ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size())); - EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), - buf.size()), - 0); - } - - // Assert that the last packet is dropped because the receive buffer should - // be full after the first four packets. - std::vector<char> recv_buf(buf.size() + HdrLen()); - struct iovec iov = {}; - iov.iov_base = static_cast<void*>(const_cast<char*>(recv_buf.data())); - iov.iov_len = buf.size(); - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - ASSERT_THAT(RetryEINTR(recvmsg)(s_, &msg, MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); - } -} - -void RawSocketTest::SendBuf(const char* buf, int buf_len) { - // It's safe to use const_cast here because sendmsg won't modify the iovec or - // address. - struct iovec iov = {}; - iov.iov_base = static_cast<void*>(const_cast<char*>(buf)); - iov.iov_len = static_cast<size_t>(buf_len); - struct msghdr msg = {}; - msg.msg_name = static_cast<void*>(&addr_); - msg.msg_namelen = AddrLen(); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - ASSERT_THAT(sendmsg(s_, &msg, 0), SyscallSucceedsWithValue(buf_len)); -} - -void RawSocketTest::ReceiveBuf(char* recv_buf, size_t recv_buf_len) { - ASSERT_NO_FATAL_FAILURE(ReceiveBufFrom(s_, recv_buf, recv_buf_len)); -} - -void RawSocketTest::ReceiveBufFrom(int sock, char* recv_buf, - size_t recv_buf_len) { - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sock, recv_buf, recv_buf_len)); -} - -TEST_P(RawSocketTest, SetSocketDetachFilterNoInstalledFilter) { - // TODO(gvisor.dev/2746): Support SO_ATTACH_FILTER/SO_DETACH_FILTER. - if (IsRunningOnGvisor()) { - constexpr int val = 0; - ASSERT_THAT(setsockopt(s_, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallSucceeds()); - return; - } - - constexpr int val = 0; - ASSERT_THAT(setsockopt(s_, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallFailsWithErrno(ENOENT)); -} - -TEST_P(RawSocketTest, GetSocketDetachFilter) { - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_DETACH_FILTER, &val, &val_len), - SyscallFailsWithErrno(ENOPROTOOPT)); -} - -// AF_INET6+SOCK_RAW+IPPROTO_RAW sockets can be created, but not written to. -TEST(RawSocketTest, IPv6ProtoRaw) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW), - SyscallSucceeds()); - - // Verify that writing yields EINVAL. - char buf[] = "This is such a weird little edge case"; - struct sockaddr_in6 sin6 = {}; - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = in6addr_loopback; - ASSERT_THAT(sendto(sock, buf, sizeof(buf), 0 /* flags */, - reinterpret_cast<struct sockaddr*>(&sin6), sizeof(sin6)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(RawSocketTest, IPv6SendMsg) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP), - SyscallSucceeds()); - - char kBuf[] = "hello"; - struct iovec iov = {}; - iov.iov_base = static_cast<void*>(const_cast<char*>(kBuf)); - iov.iov_len = static_cast<size_t>(sizeof(kBuf)); - - struct sockaddr_storage addr = {}; - struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&addr); - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - struct msghdr msg = {}; - msg.msg_name = static_cast<void*>(&addr); - msg.msg_namelen = sizeof(sockaddr_in); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(RawSocketTest, ConnectOnIPv6Socket) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int sock; - ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP), - SyscallSucceeds()); - - struct sockaddr_storage addr = {}; - struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&addr); - sin->sin_family = AF_INET; - sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - ASSERT_THAT(connect(sock, reinterpret_cast<struct sockaddr*>(&addr), - sizeof(sockaddr_in6)), - SyscallFailsWithErrno(EAFNOSUPPORT)); -} - -INSTANTIATE_TEST_SUITE_P( - AllInetTests, RawSocketTest, - ::testing::Combine(::testing::Values(IPPROTO_TCP, IPPROTO_UDP), - ::testing::Values(AF_INET, AF_INET6))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc deleted file mode 100644 index 2f25aceb2..000000000 --- a/test/syscalls/linux/raw_socket_hdrincl.cc +++ /dev/null @@ -1,412 +0,0 @@ -// Copyright 2019 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 <linux/capability.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <netinet/udp.h> -#include <poll.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <cstring> - -#include "gtest/gtest.h" -#include "absl/base/internal/endian.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Tests for IPPROTO_RAW raw sockets, which implies IP_HDRINCL. -class RawHDRINCL : public ::testing::Test { - protected: - // Creates a socket to be used in tests. - void SetUp() override; - - // Closes the socket created by SetUp(). - void TearDown() override; - - // Returns a valid looback IP header with no payload. - struct iphdr LoopbackHeader(); - - // Fills in buf with an IP header, UDP header, and payload. Returns false if - // buf_size isn't large enough to hold everything. - bool FillPacket(char* buf, size_t buf_size, int port, const char* payload, - uint16_t payload_size); - - // The socket used for both reading and writing. - int socket_; - - // The loopback address. - struct sockaddr_in addr_; -}; - -void RawHDRINCL::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_RAW), - SyscallFailsWithErrno(EPERM)); - GTEST_SKIP(); - } - - ASSERT_THAT(socket_ = socket(AF_INET, SOCK_RAW, IPPROTO_RAW), - SyscallSucceeds()); - - addr_ = {}; - - addr_.sin_port = IPPROTO_IP; - addr_.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr_.sin_family = AF_INET; -} - -void RawHDRINCL::TearDown() { - // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - EXPECT_THAT(close(socket_), SyscallSucceeds()); - } -} - -struct iphdr RawHDRINCL::LoopbackHeader() { - struct iphdr hdr = {}; - hdr.ihl = 5; - hdr.version = 4; - hdr.tos = 0; - hdr.tot_len = absl::gbswap_16(sizeof(hdr)); - hdr.id = 0; - hdr.frag_off = 0; - hdr.ttl = 7; - hdr.protocol = 1; - hdr.daddr = htonl(INADDR_LOOPBACK); - // hdr.check is set by the network stack. - // hdr.tot_len is set by the network stack. - // hdr.saddr is set by the network stack. - return hdr; -} - -bool RawHDRINCL::FillPacket(char* buf, size_t buf_size, int port, - const char* payload, uint16_t payload_size) { - if (buf_size < sizeof(struct iphdr) + sizeof(struct udphdr) + payload_size) { - return false; - } - - struct iphdr ip = LoopbackHeader(); - ip.protocol = IPPROTO_UDP; - - struct udphdr udp = {}; - udp.source = absl::gbswap_16(port); - udp.dest = absl::gbswap_16(port); - udp.len = absl::gbswap_16(sizeof(udp) + payload_size); - udp.check = 0; - - memcpy(buf, reinterpret_cast<char*>(&ip), sizeof(ip)); - memcpy(buf + sizeof(ip), reinterpret_cast<char*>(&udp), sizeof(udp)); - memcpy(buf + sizeof(ip) + sizeof(udp), payload, payload_size); - - return true; -} - -// We should be able to create multiple IPPROTO_RAW sockets. RawHDRINCL::Setup -// creates the first one, so we only have to create one more here. -TEST_F(RawHDRINCL, MultipleCreation) { - int s2; - ASSERT_THAT(s2 = socket(AF_INET, SOCK_RAW, IPPROTO_RAW), SyscallSucceeds()); - - ASSERT_THAT(close(s2), SyscallSucceeds()); -} - -// Test that shutting down an unconnected socket fails. -TEST_F(RawHDRINCL, FailShutdownWithoutConnect) { - ASSERT_THAT(shutdown(socket_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); - ASSERT_THAT(shutdown(socket_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); -} - -// Test that listen() fails. -TEST_F(RawHDRINCL, FailListen) { - ASSERT_THAT(listen(socket_, 1), SyscallFailsWithErrno(ENOTSUP)); -} - -// Test that accept() fails. -TEST_F(RawHDRINCL, FailAccept) { - struct sockaddr saddr; - socklen_t addrlen; - ASSERT_THAT(accept(socket_, &saddr, &addrlen), - SyscallFailsWithErrno(ENOTSUP)); -} - -// Test that the socket is writable immediately. -TEST_F(RawHDRINCL, PollWritableImmediately) { - struct pollfd pfd = {}; - pfd.fd = socket_; - pfd.events = POLLOUT; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 0), SyscallSucceedsWithValue(1)); -} - -// Test that the socket isn't readable. -TEST_F(RawHDRINCL, NotReadable) { - // Try to receive data with MSG_DONTWAIT, which returns immediately if there's - // nothing to be read. - char buf[117]; - ASSERT_THAT(RetryEINTR(recv)(socket_, buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Test that we can connect() to a valid IP (loopback). -TEST_F(RawHDRINCL, ConnectToLoopback) { - ASSERT_THAT(connect(socket_, reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallSucceeds()); -} - -TEST_F(RawHDRINCL, SendWithoutConnectSucceeds) { - // FIXME(gvisor.dev/issue/3159): Test currently flaky. - SKIP_IF(true); - - struct iphdr hdr = LoopbackHeader(); - ASSERT_THAT(send(socket_, &hdr, sizeof(hdr), 0), - SyscallSucceedsWithValue(sizeof(hdr))); -} - -// HDRINCL implies write-only. Verify that we can't read a packet sent to -// loopback. -TEST_F(RawHDRINCL, NotReadableAfterWrite) { - ASSERT_THAT(connect(socket_, reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallSucceeds()); - - // Construct a packet with an IP header, UDP header, and payload. - constexpr char kPayload[] = "odst"; - char packet[sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kPayload)]; - ASSERT_TRUE(FillPacket(packet, sizeof(packet), 40000 /* port */, kPayload, - sizeof(kPayload))); - - socklen_t addrlen = sizeof(addr_); - ASSERT_NO_FATAL_FAILURE( - sendto(socket_, reinterpret_cast<void*>(&packet), sizeof(packet), 0, - reinterpret_cast<struct sockaddr*>(&addr_), addrlen)); - - struct pollfd pfd = {}; - pfd.fd = socket_; - pfd.events = POLLIN; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0)); -} - -TEST_F(RawHDRINCL, WriteTooSmall) { - ASSERT_THAT(connect(socket_, reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallSucceeds()); - - // This is smaller than the size of an IP header. - constexpr char kBuf[] = "JP5"; - ASSERT_THAT(send(socket_, kBuf, sizeof(kBuf), 0), - SyscallFailsWithErrno(EINVAL)); -} - -// Bind to localhost. -TEST_F(RawHDRINCL, BindToLocalhost) { - ASSERT_THAT( - bind(socket_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), - SyscallSucceeds()); -} - -// Bind to a different address. -TEST_F(RawHDRINCL, BindToInvalid) { - struct sockaddr_in bind_addr = {}; - bind_addr.sin_family = AF_INET; - bind_addr.sin_addr = {1}; // 1.0.0.0 - An address that we can't bind to. - ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr)), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -// Send and receive a packet. -TEST_F(RawHDRINCL, SendAndReceive) { - int port = 40000; - if (!IsRunningOnGvisor()) { - port = static_cast<short>(ASSERT_NO_ERRNO_AND_VALUE( - PortAvailable(0, AddressFamily::kIpv4, SocketType::kUdp, false))); - } - - // IPPROTO_RAW sockets are write-only. We'll have to open another socket to - // read what we write. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP)); - - // Construct a packet with an IP header, UDP header, and payload. - constexpr char kPayload[] = "toto"; - char packet[sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kPayload)]; - ASSERT_TRUE( - FillPacket(packet, sizeof(packet), port, kPayload, sizeof(kPayload))); - - socklen_t addrlen = sizeof(addr_); - ASSERT_NO_FATAL_FAILURE(sendto(socket_, &packet, sizeof(packet), 0, - reinterpret_cast<struct sockaddr*>(&addr_), - addrlen)); - - // Receive the payload. - char recv_buf[sizeof(packet)]; - struct sockaddr_in src; - socklen_t src_size = sizeof(src); - ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), 0, - reinterpret_cast<struct sockaddr*>(&src), &src_size), - SyscallSucceedsWithValue(sizeof(packet))); - EXPECT_EQ( - memcmp(kPayload, recv_buf + sizeof(struct iphdr) + sizeof(struct udphdr), - sizeof(kPayload)), - 0); - // The network stack should have set the source address. - EXPECT_EQ(src.sin_family, AF_INET); - EXPECT_EQ(absl::gbswap_32(src.sin_addr.s_addr), INADDR_LOOPBACK); - // The packet ID should not be 0, as the packet has DF=0. - struct iphdr* iphdr = reinterpret_cast<struct iphdr*>(recv_buf); - EXPECT_NE(iphdr->id, 0); -} - -// Send and receive a packet where the sendto address is not the same as the -// provided destination. -TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) { - // FIXME(gvisor.dev/issue/3160): Test currently flaky. - SKIP_IF(true); - - int port = 40000; - if (!IsRunningOnGvisor()) { - port = static_cast<short>(ASSERT_NO_ERRNO_AND_VALUE( - PortAvailable(0, AddressFamily::kIpv4, SocketType::kUdp, false))); - } - - // IPPROTO_RAW sockets are write-only. We'll have to open another socket to - // read what we write. - FileDescriptor udp_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP)); - - // Construct a packet with an IP header, UDP header, and payload. - constexpr char kPayload[] = "toto"; - char packet[sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kPayload)]; - ASSERT_TRUE( - FillPacket(packet, sizeof(packet), port, kPayload, sizeof(kPayload))); - // Overwrite the IP destination address with an IP we can't get to. - struct iphdr iphdr = {}; - memcpy(&iphdr, packet, sizeof(iphdr)); - iphdr.daddr = 42; - memcpy(packet, &iphdr, sizeof(iphdr)); - - socklen_t addrlen = sizeof(addr_); - ASSERT_NO_FATAL_FAILURE(sendto(socket_, &packet, sizeof(packet), 0, - reinterpret_cast<struct sockaddr*>(&addr_), - addrlen)); - - // Receive the payload, since sendto should replace the bad destination with - // localhost. - char recv_buf[sizeof(packet)]; - struct sockaddr_in src; - socklen_t src_size = sizeof(src); - ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), 0, - reinterpret_cast<struct sockaddr*>(&src), &src_size), - SyscallSucceedsWithValue(sizeof(packet))); - EXPECT_EQ( - memcmp(kPayload, recv_buf + sizeof(struct iphdr) + sizeof(struct udphdr), - sizeof(kPayload)), - 0); - // The network stack should have set the source address. - EXPECT_EQ(src.sin_family, AF_INET); - EXPECT_EQ(absl::gbswap_32(src.sin_addr.s_addr), INADDR_LOOPBACK); - // The packet ID should not be 0, as the packet has DF=0. - struct iphdr recv_iphdr = {}; - memcpy(&recv_iphdr, recv_buf, sizeof(recv_iphdr)); - EXPECT_NE(recv_iphdr.id, 0); - // The destination address should be localhost, not the bad IP we set - // initially. - EXPECT_EQ(absl::gbswap_32(recv_iphdr.daddr), INADDR_LOOPBACK); -} - -// Send and receive a packet w/ the IP_HDRINCL option set. -TEST_F(RawHDRINCL, SendAndReceiveIPHdrIncl) { - int port = 40000; - if (!IsRunningOnGvisor()) { - port = static_cast<short>(ASSERT_NO_ERRNO_AND_VALUE( - PortAvailable(0, AddressFamily::kIpv4, SocketType::kUdp, false))); - } - - FileDescriptor recv_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP)); - - FileDescriptor send_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP)); - - // Enable IP_HDRINCL option so that we can build and send w/ an IP - // header. - constexpr int kSockOptOn = 1; - ASSERT_THAT(setsockopt(send_sock.get(), SOL_IP, IP_HDRINCL, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - // This is not strictly required but we do it to make sure that setting - // IP_HDRINCL on a non IPPROTO_RAW socket does not prevent it from receiving - // packets. - ASSERT_THAT(setsockopt(recv_sock.get(), SOL_IP, IP_HDRINCL, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Construct a packet with an IP header, UDP header, and payload. - constexpr char kPayload[] = "toto"; - char packet[sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kPayload)]; - ASSERT_TRUE( - FillPacket(packet, sizeof(packet), port, kPayload, sizeof(kPayload))); - - socklen_t addrlen = sizeof(addr_); - ASSERT_NO_FATAL_FAILURE(sendto(send_sock.get(), &packet, sizeof(packet), 0, - reinterpret_cast<struct sockaddr*>(&addr_), - addrlen)); - - // Receive the payload. - char recv_buf[sizeof(packet)]; - struct sockaddr_in src; - socklen_t src_size = sizeof(src); - ASSERT_THAT(recvfrom(recv_sock.get(), recv_buf, sizeof(recv_buf), 0, - reinterpret_cast<struct sockaddr*>(&src), &src_size), - SyscallSucceedsWithValue(sizeof(packet))); - EXPECT_EQ( - memcmp(kPayload, recv_buf + sizeof(struct iphdr) + sizeof(struct udphdr), - sizeof(kPayload)), - 0); - // The network stack should have set the source address. - EXPECT_EQ(src.sin_family, AF_INET); - EXPECT_EQ(absl::gbswap_32(src.sin_addr.s_addr), INADDR_LOOPBACK); - struct iphdr iphdr = {}; - memcpy(&iphdr, recv_buf, sizeof(iphdr)); - EXPECT_NE(iphdr.id, 0); - - // Also verify that the packet we just sent was not delivered to the - // IPPROTO_RAW socket. - { - char recv_buf[sizeof(packet)]; - struct sockaddr_in src; - socklen_t src_size = sizeof(src); - ASSERT_THAT(recvfrom(socket_, recv_buf, sizeof(recv_buf), MSG_DONTWAIT, - reinterpret_cast<struct sockaddr*>(&src), &src_size), - SyscallFailsWithErrno(EAGAIN)); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc deleted file mode 100644 index bd779da92..000000000 --- a/test/syscalls/linux/raw_socket_icmp.cc +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2019 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 <linux/capability.h> -#include <netinet/in.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <algorithm> -#include <cstdint> - -#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/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// The size of an empty ICMP packet and IP header together. -constexpr size_t kEmptyICMPSize = 28; - -// ICMP raw sockets get their own special tests because Linux automatically -// responds to ICMP echo requests, and thus a single echo request sent via -// loopback leads to 2 received ICMP packets. - -class RawSocketICMPTest : public ::testing::Test { - protected: - // Creates a socket to be used in tests. - void SetUp() override; - - // Closes the socket created by SetUp(). - void TearDown() override; - - // Checks that both an ICMP echo request and reply are received. Calls should - // be wrapped in ASSERT_NO_FATAL_FAILURE. - void ExpectICMPSuccess(const struct icmphdr& icmp); - - // Sends icmp via s_. - void SendEmptyICMP(const struct icmphdr& icmp); - - // Sends icmp via s_ to the given address. - void SendEmptyICMPTo(int sock, const struct sockaddr_in& addr, - const struct icmphdr& icmp); - - // Reads from s_ into recv_buf. - void ReceiveICMP(char* recv_buf, size_t recv_buf_len, size_t expected_size, - struct sockaddr_in* src); - - // Reads from sock into recv_buf. - void ReceiveICMPFrom(char* recv_buf, size_t recv_buf_len, - size_t expected_size, struct sockaddr_in* src, int sock); - - // The socket used for both reading and writing. - int s_; - - // The loopback address. - struct sockaddr_in addr_; -}; - -void RawSocketICMPTest::SetUp() { - if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), - SyscallFailsWithErrno(EPERM)); - GTEST_SKIP(); - } - - ASSERT_THAT(s_ = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP), SyscallSucceeds()); - - addr_ = {}; - - // "On raw sockets sin_port is set to the IP protocol." - ip(7). - addr_.sin_port = IPPROTO_IP; - addr_.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr_.sin_family = AF_INET; -} - -void RawSocketICMPTest::TearDown() { - // TearDown will be run even if we skip the test. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) { - EXPECT_THAT(close(s_), SyscallSucceeds()); - } -} - -// We'll only read an echo in this case, as the kernel won't respond to the -// malformed ICMP checksum. -TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence, - // and ID. None of that should matter for raw sockets - the kernel should - // still give us the packet. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.checksum = 0; - icmp.un.echo.sequence = 2012; - icmp.un.echo.id = 2014; - ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp)); - - // Veryify that we get the echo, then that there's nothing else to read. - char recv_buf[kEmptyICMPSize]; - struct sockaddr_in src; - ASSERT_NO_FATAL_FAILURE( - ReceiveICMP(recv_buf, sizeof(recv_buf), sizeof(struct icmphdr), &src)); - EXPECT_EQ(memcmp(&src, &addr_, sizeof(src)), 0); - // The packet should be identical to what we sent. - EXPECT_EQ(memcmp(recv_buf + sizeof(struct iphdr), &icmp, sizeof(icmp)), 0); - - // And there should be nothing left to read. - EXPECT_THAT(RetryEINTR(recv)(s_, recv_buf, sizeof(recv_buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Send and receive an ICMP packet. -TEST_F(RawSocketICMPTest, SendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - // Prepare and send an ICMP packet. Use arbitrary junk for sequence and ID. - // None of that should matter for raw sockets - the kernel should still give - // us the packet. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.checksum = 0; - icmp.un.echo.sequence = 2012; - icmp.un.echo.id = 2014; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp)); - - ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp)); -} - -// We should be able to create multiple raw sockets for the same protocol and -// receive the same packet on both. -TEST_F(RawSocketICMPTest, MultipleSocketReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - FileDescriptor s2 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)); - - // Prepare and send an ICMP packet. Use arbitrary junk for sequence and ID. - // None of that should matter for raw sockets - the kernel should still give - // us the packet. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.checksum = 0; - icmp.un.echo.sequence = 2016; - icmp.un.echo.id = 2018; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp)); - - // Both sockets will receive the echo request and reply in indeterminate - // order, so we'll need to read 2 packets from each. - - // Receive on socket 1. - constexpr int kBufSize = kEmptyICMPSize; - char recv_buf1[2][kBufSize]; - struct sockaddr_in src; - for (int i = 0; i < 2; i++) { - ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf1[i], - ABSL_ARRAYSIZE(recv_buf1[i]), - sizeof(struct icmphdr), &src)); - EXPECT_EQ(memcmp(&src, &addr_, sizeof(src)), 0); - } - - // Receive on socket 2. - char recv_buf2[2][kBufSize]; - for (int i = 0; i < 2; i++) { - ASSERT_NO_FATAL_FAILURE( - ReceiveICMPFrom(recv_buf2[i], ABSL_ARRAYSIZE(recv_buf2[i]), - sizeof(struct icmphdr), &src, s2.get())); - EXPECT_EQ(memcmp(&src, &addr_, sizeof(src)), 0); - } - - // Ensure both sockets receive identical packets. - int types[] = {ICMP_ECHO, ICMP_ECHOREPLY}; - for (int type : types) { - auto match_type = [=](char buf[kBufSize]) { - struct icmphdr* icmp = - reinterpret_cast<struct icmphdr*>(buf + sizeof(struct iphdr)); - return icmp->type == type; - }; - auto icmp1_it = - std::find_if(std::begin(recv_buf1), std::end(recv_buf1), match_type); - auto icmp2_it = - std::find_if(std::begin(recv_buf2), std::end(recv_buf2), match_type); - ASSERT_NE(icmp1_it, std::end(recv_buf1)); - ASSERT_NE(icmp2_it, std::end(recv_buf2)); - EXPECT_EQ(memcmp(*icmp1_it + sizeof(struct iphdr), - *icmp2_it + sizeof(struct iphdr), sizeof(icmp)), - 0); - } -} - -// A raw ICMP socket and ping socket should both receive the ICMP packets -// intended for the ping socket. -TEST_F(RawSocketICMPTest, RawAndPingSockets) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - FileDescriptor ping_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); - - // Ping sockets take care of the ICMP ID and checksum. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.un.echo.sequence = *static_cast<unsigned short*>(&icmp.un.echo.sequence); - ASSERT_THAT(RetryEINTR(sendto)(ping_sock.get(), &icmp, sizeof(icmp), 0, - reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallSucceedsWithValue(sizeof(icmp))); - - // Receive on socket 1, which receives the echo request and reply in - // indeterminate order. - constexpr int kBufSize = kEmptyICMPSize; - char recv_buf1[2][kBufSize]; - struct sockaddr_in src; - for (int i = 0; i < 2; i++) { - ASSERT_NO_FATAL_FAILURE( - ReceiveICMP(recv_buf1[i], kBufSize, sizeof(struct icmphdr), &src)); - EXPECT_EQ(memcmp(&src, &addr_, sizeof(src)), 0); - } - - // Receive on socket 2. Ping sockets only get the echo reply, not the initial - // echo. - char ping_recv_buf[kBufSize]; - ASSERT_THAT(RetryEINTR(recv)(ping_sock.get(), ping_recv_buf, kBufSize, 0), - SyscallSucceedsWithValue(sizeof(struct icmphdr))); - - // Ensure both sockets receive identical echo reply packets. - auto match_type_raw = [=](char buf[kBufSize]) { - struct icmphdr* icmp = - reinterpret_cast<struct icmphdr*>(buf + sizeof(struct iphdr)); - return icmp->type == ICMP_ECHOREPLY; - }; - auto raw_reply_it = - std::find_if(std::begin(recv_buf1), std::end(recv_buf1), match_type_raw); - ASSERT_NE(raw_reply_it, std::end(recv_buf1)); - EXPECT_EQ( - memcmp(*raw_reply_it + sizeof(struct iphdr), ping_recv_buf, sizeof(icmp)), - 0); -} - -// A raw ICMP socket should be able to send a malformed short ICMP Echo Request, -// while ping socket should not. -// Neither should be able to receieve a short malformed packet. -TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - FileDescriptor ping_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); - - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.un.echo.sequence = 0; - icmp.un.echo.id = 6789; - icmp.checksum = 0; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - - // Omit 2 bytes from ICMP packet. - constexpr int kShortICMPSize = sizeof(icmp) - 2; - - // Sending a malformed short ICMP message to a ping socket should fail. - ASSERT_THAT(RetryEINTR(sendto)(ping_sock.get(), &icmp, kShortICMPSize, 0, - reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallFailsWithErrno(EINVAL)); - - // Sending a malformed short ICMP message to a raw socket should not fail. - ASSERT_THAT(RetryEINTR(sendto)(s_, &icmp, kShortICMPSize, 0, - reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallSucceedsWithValue(kShortICMPSize)); - - // Neither Ping nor Raw socket should have anything to read. - char recv_buf[kEmptyICMPSize]; - EXPECT_THAT(RetryEINTR(recv)(ping_sock.get(), recv_buf, sizeof(recv_buf), - MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); - EXPECT_THAT(RetryEINTR(recv)(s_, recv_buf, sizeof(recv_buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// A raw ICMP socket should be able to send a malformed short ICMP Echo Reply, -// while ping socket should not. -// Neither should be able to receieve a short malformed packet. -TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - FileDescriptor ping_sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP)); - - struct icmphdr icmp; - icmp.type = ICMP_ECHOREPLY; - icmp.code = 0; - icmp.un.echo.sequence = 0; - icmp.un.echo.id = 6789; - icmp.checksum = 0; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - - // Omit 2 bytes from ICMP packet. - constexpr int kShortICMPSize = sizeof(icmp) - 2; - - // Sending a malformed short ICMP message to a ping socket should fail. - ASSERT_THAT(RetryEINTR(sendto)(ping_sock.get(), &icmp, kShortICMPSize, 0, - reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallFailsWithErrno(EINVAL)); - - // Sending a malformed short ICMP message to a raw socket should not fail. - ASSERT_THAT(RetryEINTR(sendto)(s_, &icmp, kShortICMPSize, 0, - reinterpret_cast<struct sockaddr*>(&addr_), - sizeof(addr_)), - SyscallSucceedsWithValue(kShortICMPSize)); - - // Neither Ping nor Raw socket should have anything to read. - char recv_buf[kEmptyICMPSize]; - EXPECT_THAT(RetryEINTR(recv)(ping_sock.get(), recv_buf, sizeof(recv_buf), - MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); - EXPECT_THAT(RetryEINTR(recv)(s_, recv_buf, sizeof(recv_buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Test that connect() sends packets to the right place. -TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), - SyscallSucceeds()); - - // Prepare and send an ICMP packet. Use arbitrary junk for sequence and ID. - // None of that should matter for raw sockets - the kernel should still give - // us the packet. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.checksum = 0; - icmp.un.echo.sequence = 2003; - icmp.un.echo.id = 2004; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - ASSERT_THAT(send(s_, &icmp, sizeof(icmp), 0), - SyscallSucceedsWithValue(sizeof(icmp))); - - ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp)); -} - -// Bind to localhost, then send and receive packets. -TEST_F(RawSocketICMPTest, BindSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), - SyscallSucceeds()); - - // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence, - // and ID. None of that should matter for raw sockets - the kernel should - // still give us the packet. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.checksum = 0; - icmp.un.echo.sequence = 2004; - icmp.un.echo.id = 2007; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp)); - - ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp)); -} - -// Bind and connect to localhost and send/receive packets. -TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - ASSERT_THAT( - bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), - SyscallSucceeds()); - ASSERT_THAT( - connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)), - SyscallSucceeds()); - - // Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence, - // and ID. None of that should matter for raw sockets - the kernel should - // still give us the packet. - struct icmphdr icmp; - icmp.type = ICMP_ECHO; - icmp.code = 0; - icmp.checksum = 0; - icmp.un.echo.sequence = 2010; - icmp.un.echo.id = 7; - icmp.checksum = ICMPChecksum(icmp, NULL, 0); - ASSERT_NO_FATAL_FAILURE(SendEmptyICMP(icmp)); - - ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp)); -} - -// Set and get SO_LINGER. -TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int level = SOL_SOCKET; - int type = SO_LINGER; - - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = 5; - ASSERT_THAT(setsockopt(s_, level, type, &sl, sizeof(sl)), - SyscallSucceedsWithValue(0)); - - struct linger got_linger = {}; - socklen_t length = sizeof(sl); - ASSERT_THAT(getsockopt(s_, level, type, &got_linger, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, length)); -} - -// Test getsockopt for SO_ACCEPTCONN. -TEST_F(RawSocketICMPTest, GetSocketAcceptConn) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); - - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); -} - -void RawSocketICMPTest::ExpectICMPSuccess(const struct icmphdr& icmp) { - // We're going to receive both the echo request and reply, but the order is - // indeterminate. - char recv_buf[kEmptyICMPSize]; - struct sockaddr_in src; - bool received_request = false; - bool received_reply = false; - - for (int i = 0; i < 2; i++) { - // Receive the packet. - ASSERT_NO_FATAL_FAILURE(ReceiveICMP(recv_buf, ABSL_ARRAYSIZE(recv_buf), - sizeof(struct icmphdr), &src)); - EXPECT_EQ(memcmp(&src, &addr_, sizeof(src)), 0); - struct icmphdr* recvd_icmp = - reinterpret_cast<struct icmphdr*>(recv_buf + sizeof(struct iphdr)); - switch (recvd_icmp->type) { - case ICMP_ECHO: - EXPECT_FALSE(received_request); - received_request = true; - // The packet should be identical to what we sent. - EXPECT_EQ(memcmp(recv_buf + sizeof(struct iphdr), &icmp, sizeof(icmp)), - 0); - break; - - case ICMP_ECHOREPLY: - EXPECT_FALSE(received_reply); - received_reply = true; - // Most fields should be the same. - EXPECT_EQ(recvd_icmp->code, icmp.code); - EXPECT_EQ(recvd_icmp->un.echo.sequence, icmp.un.echo.sequence); - EXPECT_EQ(recvd_icmp->un.echo.id, icmp.un.echo.id); - // A couple are different. - EXPECT_EQ(recvd_icmp->type, ICMP_ECHOREPLY); - // The checksum computed over the reply should still be valid. - EXPECT_EQ(ICMPChecksum(*recvd_icmp, NULL, 0), 0); - break; - } - } - - ASSERT_TRUE(received_request); - ASSERT_TRUE(received_reply); -} - -void RawSocketICMPTest::SendEmptyICMP(const struct icmphdr& icmp) { - ASSERT_NO_FATAL_FAILURE(SendEmptyICMPTo(s_, addr_, icmp)); -} - -void RawSocketICMPTest::SendEmptyICMPTo(int sock, - const struct sockaddr_in& addr, - const struct icmphdr& icmp) { - // It's safe to use const_cast here because sendmsg won't modify the iovec or - // address. - struct iovec iov = {}; - iov.iov_base = static_cast<void*>(const_cast<struct icmphdr*>(&icmp)); - iov.iov_len = sizeof(icmp); - struct msghdr msg = {}; - msg.msg_name = static_cast<void*>(const_cast<struct sockaddr_in*>(&addr)); - msg.msg_namelen = sizeof(addr); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallSucceedsWithValue(sizeof(icmp))); -} - -void RawSocketICMPTest::ReceiveICMP(char* recv_buf, size_t recv_buf_len, - size_t expected_size, - struct sockaddr_in* src) { - ASSERT_NO_FATAL_FAILURE( - ReceiveICMPFrom(recv_buf, recv_buf_len, expected_size, src, s_)); -} - -void RawSocketICMPTest::ReceiveICMPFrom(char* recv_buf, size_t recv_buf_len, - size_t expected_size, - struct sockaddr_in* src, int sock) { - struct iovec iov = {}; - iov.iov_base = recv_buf; - iov.iov_len = recv_buf_len; - struct msghdr msg = {}; - msg.msg_name = src; - msg.msg_namelen = sizeof(*src); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = NULL; - msg.msg_controllen = 0; - msg.msg_flags = 0; - // We should receive the ICMP packet plus 20 bytes of IP header. - ASSERT_THAT(recvmsg(sock, &msg, 0), - SyscallSucceedsWithValue(expected_size + sizeof(struct iphdr))); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/read.cc b/test/syscalls/linux/read.cc deleted file mode 100644 index 087262535..000000000 --- a/test/syscalls/linux/read.cc +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/base/macros.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 { - -class ReadTest : public ::testing::Test { - void SetUp() override { - name_ = NewTempAbsPath(); - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_CREAT, 0644), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - } - - void TearDown() override { unlink(name_.c_str()); } - - public: - std::string name_; -}; - -TEST_F(ReadTest, ZeroBuffer) { - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_RDWR), SyscallSucceeds()); - - char msg[] = "hello world"; - EXPECT_THAT(PwriteFd(fd, msg, strlen(msg), 0), - SyscallSucceedsWithValue(strlen(msg))); - - char buf[10]; - EXPECT_THAT(ReadFd(fd, buf, 0), SyscallSucceedsWithValue(0)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(ReadTest, EmptyFileReturnsZeroAtEOF) { - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_RDWR), SyscallSucceeds()); - - char eof_buf[10]; - EXPECT_THAT(ReadFd(fd, eof_buf, 10), SyscallSucceedsWithValue(0)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(ReadTest, EofAfterRead) { - int fd; - ASSERT_THAT(fd = open(name_.c_str(), O_RDWR), SyscallSucceeds()); - - // Write some bytes to be read. - constexpr char kMessage[] = "hello world"; - EXPECT_THAT(PwriteFd(fd, kMessage, sizeof(kMessage), 0), - SyscallSucceedsWithValue(sizeof(kMessage))); - - // Read all of the bytes at once. - char buf[sizeof(kMessage)]; - EXPECT_THAT(ReadFd(fd, buf, sizeof(kMessage)), - SyscallSucceedsWithValue(sizeof(kMessage))); - - // Read again with a non-zero buffer and expect EOF. - char eof_buf[10]; - EXPECT_THAT(ReadFd(fd, eof_buf, 10), SyscallSucceedsWithValue(0)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(ReadTest, DevNullReturnsEof) { - int fd; - ASSERT_THAT(fd = open("/dev/null", O_RDONLY), SyscallSucceeds()); - std::vector<char> buf(1); - EXPECT_THAT(ReadFd(fd, buf.data(), 1), SyscallSucceedsWithValue(0)); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -const int kReadSize = 128 * 1024; - -// Do not allow random save as it could lead to partial reads. -TEST_F(ReadTest, CanReadFullyFromDevZero_NoRandomSave) { - int fd; - ASSERT_THAT(fd = open("/dev/zero", O_RDONLY), SyscallSucceeds()); - - std::vector<char> buf(kReadSize, 1); - EXPECT_THAT(ReadFd(fd, buf.data(), kReadSize), - SyscallSucceedsWithValue(kReadSize)); - EXPECT_THAT(close(fd), SyscallSucceeds()); - EXPECT_EQ(std::vector<char>(kReadSize, 0), buf); -} - -TEST_F(ReadTest, ReadDirectoryFails) { - const FileDescriptor file = - ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); - std::vector<char> buf(1); - EXPECT_THAT(ReadFd(file.get(), buf.data(), 1), SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(ReadTest, ReadWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - std::vector<char> buf(1); - EXPECT_THAT(ReadFd(fd.get(), buf.data(), 1), SyscallFailsWithErrno(EBADF)); -} - -// Test that partial writes that hit SIGSEGV are correctly handled and return -// partial write. -TEST_F(ReadTest, PartialReadSIGSEGV) { - // Allocate 2 pages and remove permission from the second. - const size_t size = 2 * kPageSize; - void* addr = - mmap(0, size, PROT_WRITE | PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - ASSERT_NE(addr, MAP_FAILED); - auto cleanup = Cleanup( - [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(name_.c_str(), O_RDWR, 0666)); - for (size_t i = 0; i < 2; i++) { - EXPECT_THAT(pwrite(fd.get(), addr, size, 0), - SyscallSucceedsWithValue(size)); - } - - void* badAddr = reinterpret_cast<char*>(addr) + kPageSize; - ASSERT_THAT(mprotect(badAddr, kPageSize, PROT_NONE), SyscallSucceeds()); - - // Attempt to read to both pages. Create a non-contiguous iovec pair to - // ensure operation is done in 2 steps. - struct iovec iov[] = { - { - .iov_base = addr, - .iov_len = kPageSize, - }, - { - .iov_base = addr, - .iov_len = size, - }, - }; - EXPECT_THAT(preadv(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), - SyscallSucceedsWithValue(size)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/readahead.cc b/test/syscalls/linux/readahead.cc deleted file mode 100644 index 71073bb3c..000000000 --- a/test/syscalls/linux/readahead.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(ReadaheadTest, InvalidFD) { - EXPECT_THAT(readahead(-1, 1, 1), SyscallFailsWithErrno(EBADF)); -} - -TEST(ReadaheadTest, UnsupportedFile) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, 0)); - ASSERT_THAT(readahead(sock.get(), 1, 1), SyscallFailsWithErrno(EINVAL)); -} - -TEST(ReadaheadTest, InvalidOffset) { - // This test is not valid for some Linux Kernels. - SKIP_IF(!IsRunningOnGvisor()); - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - EXPECT_THAT(readahead(fd.get(), -1, 1), SyscallFailsWithErrno(EINVAL)); -} - -TEST(ReadaheadTest, ValidOffset) { - constexpr char kData[] = "123"; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - - // N.B. The implementation of readahead is filesystem-specific, and a file - // backed by ram may return EINVAL because there is nothing to be read. - EXPECT_THAT(readahead(fd.get(), 1, 1), AnyOf(SyscallSucceedsWithValue(0), - SyscallFailsWithErrno(EINVAL))); -} - -TEST(ReadaheadTest, PastEnd) { - constexpr char kData[] = "123"; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - // See above. - EXPECT_THAT(readahead(fd.get(), 2, 2), AnyOf(SyscallSucceedsWithValue(0), - SyscallFailsWithErrno(EINVAL))); -} - -TEST(ReadaheadTest, CrossesEnd) { - constexpr char kData[] = "123"; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - // See above. - EXPECT_THAT(readahead(fd.get(), 4, 2), AnyOf(SyscallSucceedsWithValue(0), - SyscallFailsWithErrno(EINVAL))); -} - -TEST(ReadaheadTest, WriteOnly) { - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_WRONLY)); - EXPECT_THAT(readahead(fd.get(), 0, 1), SyscallFailsWithErrno(EBADF)); -} - -TEST(ReadaheadTest, InvalidSize) { - // This test is not valid on some Linux kernels. - SKIP_IF(!IsRunningOnGvisor()); - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - EXPECT_THAT(readahead(fd.get(), 0, -1), SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/readv.cc b/test/syscalls/linux/readv.cc deleted file mode 100644 index 86808d255..000000000 --- a/test/syscalls/linux/readv.cc +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/file_base.h" -#include "test/syscalls/linux/readv_common.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class ReadvTest : public FileTest { - void SetUp() override { - FileTest::SetUp(); - - ASSERT_THAT(write(test_file_fd_.get(), kReadvTestData, kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - ASSERT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(write(test_pipe_[1], kReadvTestData, kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - } -}; - -TEST_F(ReadvTest, ReadOneBufferPerByte_File) { - ReadOneBufferPerByte(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadOneBufferPerByte_Pipe) { - ReadOneBufferPerByte(test_pipe_[0]); -} - -TEST_F(ReadvTest, ReadOneHalfAtATime_File) { - ReadOneHalfAtATime(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadOneHalfAtATime_Pipe) { - ReadOneHalfAtATime(test_pipe_[0]); -} - -TEST_F(ReadvTest, ReadAllOneBuffer_File) { - ReadAllOneBuffer(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadAllOneBuffer_Pipe) { ReadAllOneBuffer(test_pipe_[0]); } - -TEST_F(ReadvTest, ReadAllOneLargeBuffer_File) { - ReadAllOneLargeBuffer(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadAllOneLargeBuffer_Pipe) { - ReadAllOneLargeBuffer(test_pipe_[0]); -} - -TEST_F(ReadvTest, ReadBuffersOverlapping_File) { - ReadBuffersOverlapping(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadBuffersOverlapping_Pipe) { - ReadBuffersOverlapping(test_pipe_[0]); -} - -TEST_F(ReadvTest, ReadBuffersDiscontinuous_File) { - ReadBuffersDiscontinuous(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadBuffersDiscontinuous_Pipe) { - ReadBuffersDiscontinuous(test_pipe_[0]); -} - -TEST_F(ReadvTest, ReadIovecsCompletelyFilled_File) { - ReadIovecsCompletelyFilled(test_file_fd_.get()); -} - -TEST_F(ReadvTest, ReadIovecsCompletelyFilled_Pipe) { - ReadIovecsCompletelyFilled(test_pipe_[0]); -} - -TEST_F(ReadvTest, BadFileDescriptor) { - char buffer[1024]; - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = 1024; - - ASSERT_THAT(readv(-1, iov, 1024), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(ReadvTest, BadIovecsPointer_File) { - ASSERT_THAT(readv(test_file_fd_.get(), nullptr, 1), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvTest, BadIovecsPointer_Pipe) { - ASSERT_THAT(readv(test_pipe_[0], nullptr, 1), SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvTest, BadIovecBase_File) { - struct iovec iov[1]; - iov[0].iov_base = nullptr; - iov[0].iov_len = 1024; - ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvTest, BadIovecBase_Pipe) { - struct iovec iov[1]; - iov[0].iov_base = nullptr; - iov[0].iov_len = 1024; - ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvTest, ZeroIovecs_File) { - struct iovec iov[1]; - iov[0].iov_base = 0; - iov[0].iov_len = 0; - ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), SyscallSucceeds()); -} - -TEST_F(ReadvTest, ZeroIovecs_Pipe) { - struct iovec iov[1]; - iov[0].iov_base = 0; - iov[0].iov_len = 0; - ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallSucceeds()); -} - -TEST_F(ReadvTest, NotReadable_File) { - char buffer[1024]; - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = 1024; - - std::string wronly_file = NewTempAbsPath(); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(wronly_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)); - ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EBADF)); - fd.reset(); // Close before unlinking. - ASSERT_THAT(unlink(wronly_file.c_str()), SyscallSucceeds()); -} - -TEST_F(ReadvTest, NotReadable_Pipe) { - char buffer[1024]; - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = 1024; - ASSERT_THAT(readv(test_pipe_[1], iov, 1), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(ReadvTest, DirNotReadable) { - char buffer[1024]; - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = 1024; - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); - ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EISDIR)); -} - -TEST_F(ReadvTest, OffsetIncremented) { - char* buffer = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = kReadvTestDataSize; - - ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), - SyscallSucceedsWithValue(kReadvTestDataSize)); - ASSERT_THAT(lseek(test_file_fd_.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(kReadvTestDataSize)); - - free(buffer); -} - -TEST_F(ReadvTest, EndOfFile) { - char* buffer = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), - SyscallSucceedsWithValue(kReadvTestDataSize)); - free(buffer); - - buffer = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - iov[0].iov_base = buffer; - iov[0].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), SyscallSucceedsWithValue(0)); - free(buffer); -} - -TEST_F(ReadvTest, WouldBlock_Pipe) { - struct iovec iov[1]; - iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - iov[0].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_pipe_[0], iov, 1), - SyscallSucceedsWithValue(kReadvTestDataSize)); - free(iov[0].iov_base); - - iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallFailsWithErrno(EAGAIN)); - free(iov[0].iov_base); -} - -TEST_F(ReadvTest, ZeroBuffer) { - char buf[10]; - struct iovec iov[1]; - iov[0].iov_base = buf; - iov[0].iov_len = 0; - ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallSucceedsWithValue(0)); -} - -TEST_F(ReadvTest, NullIovecInNonemptyArray) { - std::vector<char> buf(kReadvTestDataSize); - struct iovec iov[2]; - iov[0].iov_base = nullptr; - iov[0].iov_len = 0; - iov[1].iov_base = buf.data(); - iov[1].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_file_fd_.get(), iov, 2), - SyscallSucceedsWithValue(kReadvTestDataSize)); -} - -TEST_F(ReadvTest, IovecOutsideTaskAddressRangeInNonemptyArray) { - std::vector<char> buf(kReadvTestDataSize); - struct iovec iov[2]; - iov[0].iov_base = reinterpret_cast<void*>(~static_cast<uintptr_t>(0)); - iov[0].iov_len = 0; - iov[1].iov_base = buf.data(); - iov[1].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_file_fd_.get(), iov, 2), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvTest, ReadvWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - char buffer[1024]; - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = 1024; - - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); - - ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EBADF)); -} - -// This test depends on the maximum extent of a single readv() syscall, so -// we can't tolerate interruption from saving. -TEST(ReadvTestNoFixture, TruncatedAtMax_NoRandomSave) { - // Ensure that we won't be interrupted by ITIMER_PROF. This is particularly - // important in environments where automated profiling tools may start - // ITIMER_PROF automatically. - struct itimerval itv = {}; - auto const cleanup_itimer = - ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_PROF, itv)); - - // From Linux's include/linux/fs.h. - size_t const MAX_RW_COUNT = INT_MAX & ~(kPageSize - 1); - - // Create an iovec array with 3 segments pointing to consecutive parts of a - // buffer. The first covers all but the last three pages, and should be - // written to in its entirety. The second covers the last page before - // MAX_RW_COUNT and the first page after; only the first page should be - // written to. The third covers the last page of the buffer, and should be - // skipped entirely. - size_t const kBufferSize = MAX_RW_COUNT + 2 * kPageSize; - size_t const kFirstOffset = MAX_RW_COUNT - kPageSize; - size_t const kSecondOffset = MAX_RW_COUNT + kPageSize; - // The buffer is too big to fit on the stack. - std::vector<char> buf(kBufferSize); - struct iovec iov[3]; - iov[0].iov_base = buf.data(); - iov[0].iov_len = kFirstOffset; - iov[1].iov_base = buf.data() + kFirstOffset; - iov[1].iov_len = kSecondOffset - kFirstOffset; - iov[2].iov_base = buf.data() + kSecondOffset; - iov[2].iov_len = kBufferSize - kSecondOffset; - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); - EXPECT_THAT(readv(fd.get(), iov, 3), SyscallSucceedsWithValue(MAX_RW_COUNT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/readv_common.cc b/test/syscalls/linux/readv_common.cc deleted file mode 100644 index 2694dc64f..000000000 --- a/test/syscalls/linux/readv_common.cc +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// MatchesStringLength checks that a tuple argument of (struct iovec *, int) -// corresponding to an iovec array and its length, contains data that matches -// the string length strlen. -MATCHER_P(MatchesStringLength, strlen, "") { - struct iovec* iovs = arg.first; - int niov = arg.second; - int offset = 0; - for (int i = 0; i < niov; i++) { - offset += iovs[i].iov_len; - } - if (offset != static_cast<int>(strlen)) { - *result_listener << offset; - return false; - } - return true; -} - -// MatchesStringValue checks that a tuple argument of (struct iovec *, int) -// corresponding to an iovec array and its length, contains data that matches -// the string value str. -MATCHER_P(MatchesStringValue, str, "") { - struct iovec* iovs = arg.first; - int len = strlen(str); - int niov = arg.second; - int offset = 0; - for (int i = 0; i < niov; i++) { - struct iovec iov = iovs[i]; - if (len < offset) { - *result_listener << "strlen " << len << " < offset " << offset; - return false; - } - if (strncmp(static_cast<char*>(iov.iov_base), &str[offset], iov.iov_len)) { - absl::string_view iovec_string(static_cast<char*>(iov.iov_base), - iov.iov_len); - *result_listener << iovec_string << " @offset " << offset; - return false; - } - offset += iov.iov_len; - } - return true; -} - -extern const char kReadvTestData[] = - "127.0.0.1 localhost" - "" - "# The following lines are desirable for IPv6 capable hosts" - "::1 ip6-localhost ip6-loopback" - "fe00::0 ip6-localnet" - "ff00::0 ip6-mcastprefix" - "ff02::1 ip6-allnodes" - "ff02::2 ip6-allrouters" - "ff02::3 ip6-allhosts" - "192.168.1.100 a" - "93.184.216.34 foo.bar.example.com xcpu"; -extern const size_t kReadvTestDataSize = sizeof(kReadvTestData); - -static void ReadAllOneProvidedBuffer(int fd, std::vector<char>* buffer) { - struct iovec iovs[1]; - iovs[0].iov_base = buffer->data(); - iovs[0].iov_len = kReadvTestDataSize; - - ASSERT_THAT(readv(fd, iovs, 1), SyscallSucceedsWithValue(kReadvTestDataSize)); - - std::pair<struct iovec*, int> iovec_desc(iovs, 1); - EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); - EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); -} - -void ReadAllOneBuffer(int fd) { - std::vector<char> buffer(kReadvTestDataSize); - ReadAllOneProvidedBuffer(fd, &buffer); -} - -void ReadAllOneLargeBuffer(int fd) { - std::vector<char> buffer(10 * kReadvTestDataSize); - ReadAllOneProvidedBuffer(fd, &buffer); -} - -void ReadOneHalfAtATime(int fd) { - int len0 = kReadvTestDataSize / 2; - int len1 = kReadvTestDataSize - len0; - std::vector<char> buffer0(len0); - std::vector<char> buffer1(len1); - - struct iovec iovs[2]; - iovs[0].iov_base = buffer0.data(); - iovs[0].iov_len = len0; - iovs[1].iov_base = buffer1.data(); - iovs[1].iov_len = len1; - - ASSERT_THAT(readv(fd, iovs, 2), SyscallSucceedsWithValue(kReadvTestDataSize)); - - std::pair<struct iovec*, int> iovec_desc(iovs, 2); - EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); - EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); -} - -void ReadOneBufferPerByte(int fd) { - std::vector<char> buffer(kReadvTestDataSize); - std::vector<struct iovec> iovs(kReadvTestDataSize); - char* buffer_ptr = buffer.data(); - struct iovec* iovs_ptr = iovs.data(); - - for (int i = 0; i < static_cast<int>(kReadvTestDataSize); i++) { - struct iovec iov = { - .iov_base = &buffer_ptr[i], - .iov_len = 1, - }; - iovs_ptr[i] = iov; - } - - ASSERT_THAT(readv(fd, iovs_ptr, kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - - std::pair<struct iovec*, int> iovec_desc(iovs.data(), kReadvTestDataSize); - EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); - EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); -} - -void ReadBuffersOverlapping(int fd) { - // overlap the first overlap_bytes. - int overlap_bytes = 8; - std::vector<char> buffer(kReadvTestDataSize); - - // overlapping causes us to get more data. - int expected_size = kReadvTestDataSize + overlap_bytes; - std::vector<char> expected(expected_size); - char* expected_ptr = expected.data(); - memcpy(expected_ptr, &kReadvTestData[overlap_bytes], overlap_bytes); - memcpy(&expected_ptr[overlap_bytes], &kReadvTestData[overlap_bytes], - kReadvTestDataSize - overlap_bytes); - - struct iovec iovs[2]; - iovs[0].iov_base = buffer.data(); - iovs[0].iov_len = overlap_bytes; - iovs[1].iov_base = buffer.data(); - iovs[1].iov_len = kReadvTestDataSize; - - ASSERT_THAT(readv(fd, iovs, 2), SyscallSucceedsWithValue(kReadvTestDataSize)); - - std::pair<struct iovec*, int> iovec_desc(iovs, 2); - EXPECT_THAT(iovec_desc, MatchesStringLength(expected_size)); - EXPECT_THAT(iovec_desc, MatchesStringValue(expected_ptr)); -} - -void ReadBuffersDiscontinuous(int fd) { - // Each iov is 1 byte separated by 1 byte. - std::vector<char> buffer(kReadvTestDataSize * 2); - std::vector<struct iovec> iovs(kReadvTestDataSize); - - char* buffer_ptr = buffer.data(); - struct iovec* iovs_ptr = iovs.data(); - - for (int i = 0; i < static_cast<int>(kReadvTestDataSize); i++) { - struct iovec iov = { - .iov_base = &buffer_ptr[i * 2], - .iov_len = 1, - }; - iovs_ptr[i] = iov; - } - - ASSERT_THAT(readv(fd, iovs_ptr, kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - - std::pair<struct iovec*, int> iovec_desc(iovs.data(), kReadvTestDataSize); - EXPECT_THAT(iovec_desc, MatchesStringLength(kReadvTestDataSize)); - EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); -} - -void ReadIovecsCompletelyFilled(int fd) { - int half = kReadvTestDataSize / 2; - std::vector<char> buffer(kReadvTestDataSize); - char* buffer_ptr = buffer.data(); - memset(buffer.data(), '\0', kReadvTestDataSize); - - struct iovec iovs[2]; - iovs[0].iov_base = buffer.data(); - iovs[0].iov_len = half; - iovs[1].iov_base = &buffer_ptr[half]; - iovs[1].iov_len = half; - - ASSERT_THAT(readv(fd, iovs, 2), SyscallSucceedsWithValue(half * 2)); - - std::pair<struct iovec*, int> iovec_desc(iovs, 2); - EXPECT_THAT(iovec_desc, MatchesStringLength(half * 2)); - EXPECT_THAT(iovec_desc, MatchesStringValue(kReadvTestData)); - - char* str = static_cast<char*>(iovs[0].iov_base); - str[iovs[0].iov_len - 1] = '\0'; - ASSERT_EQ(half - 1, strlen(str)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/readv_common.h b/test/syscalls/linux/readv_common.h deleted file mode 100644 index 2fa40c35f..000000000 --- a/test/syscalls/linux/readv_common.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_READV_COMMON_H_ -#define GVISOR_TEST_SYSCALLS_READV_COMMON_H_ - -#include <stddef.h> - -namespace gvisor { -namespace testing { - -// A NUL-terminated string containing the data used by tests using the following -// test helpers. -extern const char kReadvTestData[]; - -// The size of kReadvTestData, including the terminating NUL. -extern const size_t kReadvTestDataSize; - -// ReadAllOneBuffer asserts that it can read kReadvTestData from an fd using -// exactly one iovec. -void ReadAllOneBuffer(int fd); - -// ReadAllOneLargeBuffer asserts that it can read kReadvTestData from an fd -// using exactly one iovec containing an overly large buffer. -void ReadAllOneLargeBuffer(int fd); - -// ReadOneHalfAtATime asserts that it can read test_data_from an fd using -// exactly two iovecs that are roughly equivalent in size. -void ReadOneHalfAtATime(int fd); - -// ReadOneBufferPerByte asserts that it can read kReadvTestData from an fd -// using one iovec per byte. -void ReadOneBufferPerByte(int fd); - -// ReadBuffersOverlapping asserts that it can read kReadvTestData from an fd -// where two iovecs are overlapping. -void ReadBuffersOverlapping(int fd); - -// ReadBuffersDiscontinuous asserts that it can read kReadvTestData from an fd -// where each iovec is discontinuous from the next by 1 byte. -void ReadBuffersDiscontinuous(int fd); - -// ReadIovecsCompletelyFilled asserts that the previous iovec is completely -// filled before moving onto the next. -void ReadIovecsCompletelyFilled(int fd); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_READV_COMMON_H_ diff --git a/test/syscalls/linux/readv_socket.cc b/test/syscalls/linux/readv_socket.cc deleted file mode 100644 index dd6fb7008..000000000 --- a/test/syscalls/linux/readv_socket.cc +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/readv_common.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -class ReadvSocketTest : public ::testing::Test { - public: - void SetUp() override { - test_unix_stream_socket_[0] = -1; - test_unix_stream_socket_[1] = -1; - test_unix_dgram_socket_[0] = -1; - test_unix_dgram_socket_[1] = -1; - test_unix_seqpacket_socket_[0] = -1; - test_unix_seqpacket_socket_[1] = -1; - - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, test_unix_stream_socket_), - SyscallSucceeds()); - ASSERT_THAT(fcntl(test_unix_stream_socket_[0], F_SETFL, O_NONBLOCK), - SyscallSucceeds()); - ASSERT_THAT(socketpair(AF_UNIX, SOCK_DGRAM, 0, test_unix_dgram_socket_), - SyscallSucceeds()); - ASSERT_THAT(fcntl(test_unix_dgram_socket_[0], F_SETFL, O_NONBLOCK), - SyscallSucceeds()); - ASSERT_THAT( - socketpair(AF_UNIX, SOCK_SEQPACKET, 0, test_unix_seqpacket_socket_), - SyscallSucceeds()); - ASSERT_THAT(fcntl(test_unix_seqpacket_socket_[0], F_SETFL, O_NONBLOCK), - SyscallSucceeds()); - - ASSERT_THAT( - write(test_unix_stream_socket_[1], kReadvTestData, kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - ASSERT_THAT( - write(test_unix_dgram_socket_[1], kReadvTestData, kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - ASSERT_THAT(write(test_unix_seqpacket_socket_[1], kReadvTestData, - kReadvTestDataSize), - SyscallSucceedsWithValue(kReadvTestDataSize)); - } - - void TearDown() override { - close(test_unix_stream_socket_[0]); - close(test_unix_stream_socket_[1]); - - close(test_unix_dgram_socket_[0]); - close(test_unix_dgram_socket_[1]); - - close(test_unix_seqpacket_socket_[0]); - close(test_unix_seqpacket_socket_[1]); - } - - int test_unix_stream_socket_[2]; - int test_unix_dgram_socket_[2]; - int test_unix_seqpacket_socket_[2]; -}; - -TEST_F(ReadvSocketTest, ReadOneBufferPerByte_StreamSocket) { - ReadOneBufferPerByte(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadOneBufferPerByte_DgramSocket) { - ReadOneBufferPerByte(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadOneBufferPerByte_SeqPacketSocket) { - ReadOneBufferPerByte(test_unix_seqpacket_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadOneHalfAtATime_StreamSocket) { - ReadOneHalfAtATime(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadOneHalfAtATime_DgramSocket) { - ReadOneHalfAtATime(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadAllOneBuffer_StreamSocket) { - ReadAllOneBuffer(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadAllOneBuffer_DgramSocket) { - ReadAllOneBuffer(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadAllOneLargeBuffer_StreamSocket) { - ReadAllOneLargeBuffer(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadAllOneLargeBuffer_DgramSocket) { - ReadAllOneLargeBuffer(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadBuffersOverlapping_StreamSocket) { - ReadBuffersOverlapping(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadBuffersOverlapping_DgramSocket) { - ReadBuffersOverlapping(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadBuffersDiscontinuous_StreamSocket) { - ReadBuffersDiscontinuous(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadBuffersDiscontinuous_DgramSocket) { - ReadBuffersDiscontinuous(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadIovecsCompletelyFilled_StreamSocket) { - ReadIovecsCompletelyFilled(test_unix_stream_socket_[0]); -} - -TEST_F(ReadvSocketTest, ReadIovecsCompletelyFilled_DgramSocket) { - ReadIovecsCompletelyFilled(test_unix_dgram_socket_[0]); -} - -TEST_F(ReadvSocketTest, BadIovecsPointer_StreamSocket) { - ASSERT_THAT(readv(test_unix_stream_socket_[0], nullptr, 1), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvSocketTest, BadIovecsPointer_DgramSocket) { - ASSERT_THAT(readv(test_unix_dgram_socket_[0], nullptr, 1), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvSocketTest, BadIovecBase_StreamSocket) { - struct iovec iov[1]; - iov[0].iov_base = nullptr; - iov[0].iov_len = 1024; - ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvSocketTest, BadIovecBase_DgramSocket) { - struct iovec iov[1]; - iov[0].iov_base = nullptr; - iov[0].iov_len = 1024; - ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(ReadvSocketTest, ZeroIovecs_StreamSocket) { - struct iovec iov[1]; - iov[0].iov_base = 0; - iov[0].iov_len = 0; - ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), SyscallSucceeds()); -} - -TEST_F(ReadvSocketTest, ZeroIovecs_DgramSocket) { - struct iovec iov[1]; - iov[0].iov_base = 0; - iov[0].iov_len = 0; - ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), SyscallSucceeds()); -} - -TEST_F(ReadvSocketTest, WouldBlock_StreamSocket) { - struct iovec iov[1]; - iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - iov[0].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), - SyscallSucceedsWithValue(kReadvTestDataSize)); - free(iov[0].iov_base); - - iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - ASSERT_THAT(readv(test_unix_stream_socket_[0], iov, 1), - SyscallFailsWithErrno(EAGAIN)); - free(iov[0].iov_base); -} - -TEST_F(ReadvSocketTest, WouldBlock_DgramSocket) { - struct iovec iov[1]; - iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - iov[0].iov_len = kReadvTestDataSize; - ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), - SyscallSucceedsWithValue(kReadvTestDataSize)); - free(iov[0].iov_base); - - iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); - ASSERT_THAT(readv(test_unix_dgram_socket_[0], iov, 1), - SyscallFailsWithErrno(EAGAIN)); - free(iov[0].iov_base); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc deleted file mode 100644 index b1a813de0..000000000 --- a/test/syscalls/linux/rename.cc +++ /dev/null @@ -1,444 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <stdio.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(RenameTest, RootToAnything) { - ASSERT_THAT(rename("/", "/bin"), SyscallFailsWithErrno(EBUSY)); -} - -TEST(RenameTest, AnythingToRoot) { - ASSERT_THAT(rename("/bin", "/"), SyscallFailsWithErrno(EBUSY)); -} - -TEST(RenameTest, SourceIsAncestorOfTarget) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto subdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - ASSERT_THAT(rename(dir.path().c_str(), subdir.path().c_str()), - SyscallFailsWithErrno(EINVAL)); - - // Try an even deeper directory. - auto deep_subdir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(subdir.path())); - ASSERT_THAT(rename(dir.path().c_str(), deep_subdir.path().c_str()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(RenameTest, TargetIsAncestorOfSource) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto subdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - ASSERT_THAT(rename(subdir.path().c_str(), dir.path().c_str()), - SyscallFailsWithErrno(ENOTEMPTY)); - - // Try an even deeper directory. - auto deep_subdir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(subdir.path())); - ASSERT_THAT(rename(deep_subdir.path().c_str(), dir.path().c_str()), - SyscallFailsWithErrno(ENOTEMPTY)); -} - -TEST(RenameTest, FileToSelf) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - EXPECT_THAT(rename(f.path().c_str(), f.path().c_str()), SyscallSucceeds()); -} - -TEST(RenameTest, DirectoryToSelf) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(rename(f.path().c_str(), f.path().c_str()), SyscallSucceeds()); -} - -TEST(RenameTest, FileToSameDirectory) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - std::string const newpath = NewTempAbsPath(); - ASSERT_THAT(rename(f.path().c_str(), newpath.c_str()), SyscallSucceeds()); - std::string const oldpath = f.release(); - f.reset(newpath); - EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); - EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, DirectoryToSameDirectory) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - std::string const newpath = NewTempAbsPath(); - ASSERT_THAT(rename(dir.path().c_str(), newpath.c_str()), SyscallSucceeds()); - std::string const oldpath = dir.release(); - dir.reset(newpath); - EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); - EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, FileToParentDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir2.path())); - std::string const newpath = NewTempAbsPathInDir(dir1.path()); - ASSERT_THAT(rename(f.path().c_str(), newpath.c_str()), SyscallSucceeds()); - std::string const oldpath = f.release(); - f.reset(newpath); - EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); - EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, DirectoryToParentDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); - auto dir3 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir2.path())); - EXPECT_THAT(IsDirectory(dir3.path()), IsPosixErrorOkAndHolds(true)); - std::string const newpath = NewTempAbsPathInDir(dir1.path()); - ASSERT_THAT(rename(dir3.path().c_str(), newpath.c_str()), SyscallSucceeds()); - std::string const oldpath = dir3.release(); - dir3.reset(newpath); - EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); - EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); - EXPECT_THAT(IsDirectory(newpath), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, FileToChildDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - std::string const newpath = NewTempAbsPathInDir(dir2.path()); - ASSERT_THAT(rename(f.path().c_str(), newpath.c_str()), SyscallSucceeds()); - std::string const oldpath = f.release(); - f.reset(newpath); - EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); - EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, DirectoryToChildDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); - auto dir3 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); - std::string const newpath = NewTempAbsPathInDir(dir2.path()); - ASSERT_THAT(rename(dir3.path().c_str(), newpath.c_str()), SyscallSucceeds()); - std::string const oldpath = dir3.release(); - dir3.reset(newpath); - EXPECT_THAT(Exists(oldpath), IsPosixErrorOkAndHolds(false)); - EXPECT_THAT(Exists(newpath), IsPosixErrorOkAndHolds(true)); - EXPECT_THAT(IsDirectory(newpath), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, DirectoryToOwnChildDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir1.path())); - std::string const newpath = NewTempAbsPathInDir(dir2.path()); - ASSERT_THAT(rename(dir1.path().c_str(), newpath.c_str()), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(RenameTest, FileOverwritesFile) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - dir.path(), "first", TempPath::kDefaultFileMode)); - auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - dir.path(), "second", TempPath::kDefaultFileMode)); - ASSERT_THAT(rename(f1.path().c_str(), f2.path().c_str()), SyscallSucceeds()); - EXPECT_THAT(Exists(f1.path()), IsPosixErrorOkAndHolds(false)); - - f1.release(); - std::string f2_contents; - ASSERT_NO_ERRNO(GetContents(f2.path(), &f2_contents)); - EXPECT_EQ("first", f2_contents); -} - -TEST(RenameTest, DirectoryOverwritesDirectoryLinkCount) { - // Directory link counts are synthetic on overlay filesystems. - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))); - - auto parent1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(2)); - - auto parent2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(Links(parent2.path()), IsPosixErrorOkAndHolds(2)); - - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent1.path())); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(parent2.path())); - - EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(3)); - EXPECT_THAT(Links(parent2.path()), IsPosixErrorOkAndHolds(3)); - - ASSERT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), - SyscallSucceeds()); - - EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(2)); - EXPECT_THAT(Links(parent2.path()), IsPosixErrorOkAndHolds(3)); -} - -TEST(RenameTest, FileDoesNotExist) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string source = JoinPath(dir.path(), "source"); - const std::string dest = JoinPath(dir.path(), "dest"); - ASSERT_THAT(rename(source.c_str(), dest.c_str()), - SyscallFailsWithErrno(ENOENT)); -} - -TEST(RenameTest, FileDoesNotOverwriteDirectory) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT(rename(f.path().c_str(), dir.path().c_str()), - SyscallFailsWithErrno(EISDIR)); -} - -TEST(RenameTest, DirectoryDoesNotOverwriteFile) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT(rename(dir.path().c_str(), f.path().c_str()), - SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(RenameTest, DirectoryOverwritesEmptyDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), - SyscallSucceeds()); - EXPECT_THAT(Exists(dir1.path()), IsPosixErrorOkAndHolds(false)); - dir1.release(); - EXPECT_THAT(Exists(JoinPath(dir2.path(), Basename(f.path()))), - IsPosixErrorOkAndHolds(true)); - f.release(); -} - -TEST(RenameTest, FailsWithDots) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto dir1_dot = absl::StrCat(dir1.path(), "/."); - auto dir2_dot = absl::StrCat(dir2.path(), "/."); - auto dir1_dot_dot = absl::StrCat(dir1.path(), "/.."); - auto dir2_dot_dot = absl::StrCat(dir2.path(), "/.."); - - // Try with dot paths in the first argument - EXPECT_THAT(rename(dir1_dot.c_str(), dir2.path().c_str()), - SyscallFailsWithErrno(EBUSY)); - EXPECT_THAT(rename(dir1_dot_dot.c_str(), dir2.path().c_str()), - SyscallFailsWithErrno(EBUSY)); - - // Try with dot paths in the second argument - EXPECT_THAT(rename(dir1.path().c_str(), dir2_dot.c_str()), - SyscallFailsWithErrno(EBUSY)); - EXPECT_THAT(rename(dir1.path().c_str(), dir2_dot_dot.c_str()), - SyscallFailsWithErrno(EBUSY)); -} - -TEST(RenameTest, DirectoryDoesNotOverwriteNonemptyDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir2.path())); - ASSERT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), - SyscallFailsWithErrno(ENOTEMPTY)); -} - -TEST(RenameTest, FailsWhenOldParentNotWritable) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - // dir1 is not writable. - ASSERT_THAT(chmod(dir1.path().c_str(), 0555), SyscallSucceeds()); - - std::string const newpath = NewTempAbsPathInDir(dir2.path()); - EXPECT_THAT(rename(f1.path().c_str(), newpath.c_str()), - SyscallFailsWithErrno(EACCES)); -} - -TEST(RenameTest, FailsWhenNewParentNotWritable) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - // dir2 is not writable. - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555)); - - std::string const newpath = NewTempAbsPathInDir(dir2.path()); - EXPECT_THAT(rename(f1.path().c_str(), newpath.c_str()), - SyscallFailsWithErrno(EACCES)); -} - -// Equivalent to FailsWhenNewParentNotWritable, but with a destination file -// to overwrite. -TEST(RenameTest, OverwriteFailsWhenNewParentNotWritable) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - - // dir2 is not writable. - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir2.path())); - ASSERT_THAT(chmod(dir2.path().c_str(), 0555), SyscallSucceeds()); - - EXPECT_THAT(rename(f1.path().c_str(), f2.path().c_str()), - SyscallFailsWithErrno(EACCES)); -} - -// If the parent directory of source is not accessible, rename returns EACCES -// because the user cannot determine if source exists. -TEST(RenameTest, FileDoesNotExistWhenNewParentNotExecutable) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - // No execute permission. - auto dir = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0400)); - - const std::string source = JoinPath(dir.path(), "source"); - const std::string dest = JoinPath(dir.path(), "dest"); - ASSERT_THAT(rename(source.c_str(), dest.c_str()), - SyscallFailsWithErrno(EACCES)); -} - -TEST(RenameTest, DirectoryWithOpenFdOverwritesEmptyDirectory) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Get an fd on dir1 - int fd; - ASSERT_THAT(fd = open(dir1.path().c_str(), O_DIRECTORY), SyscallSucceeds()); - auto close_f = Cleanup([fd] { - // Close the fd on f. - EXPECT_THAT(close(fd), SyscallSucceeds()); - }); - - EXPECT_THAT(rename(dir1.path().c_str(), dir2.path().c_str()), - SyscallSucceeds()); - - const std::string new_f_path = JoinPath(dir2.path(), Basename(f.path())); - - auto remove_f = Cleanup([&] { - // Delete f in its new location. - ASSERT_NO_ERRNO(Delete(new_f_path)); - f.release(); - }); - - EXPECT_THAT(Exists(dir1.path()), IsPosixErrorOkAndHolds(false)); - dir1.release(); - EXPECT_THAT(Exists(new_f_path), IsPosixErrorOkAndHolds(true)); -} - -TEST(RenameTest, FileWithOpenFd) { - TempPath root_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath dir1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); - TempPath dir2 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); - TempPath dir3 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); - - // Create file in dir1. - constexpr char kContents[] = "foo"; - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - dir1.path(), kContents, TempPath::kDefaultFileMode)); - - // Get fd on file. - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); - - // Move f to dir2. - const std::string path2 = NewTempAbsPathInDir(dir2.path()); - ASSERT_THAT(rename(f.path().c_str(), path2.c_str()), SyscallSucceeds()); - - // Read f's kContents. - char buf[sizeof(kContents)]; - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(kContents), 0), - SyscallSucceedsWithValue(sizeof(kContents) - 1)); - EXPECT_EQ(absl::string_view(buf, sizeof(buf) - 1), kContents); - - // Move f to dir3. - const std::string path3 = NewTempAbsPathInDir(dir3.path()); - ASSERT_THAT(rename(path2.c_str(), path3.c_str()), SyscallSucceeds()); - - // Read f's kContents. - EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(kContents), 0), - SyscallSucceedsWithValue(sizeof(kContents) - 1)); - EXPECT_EQ(absl::string_view(buf, sizeof(buf) - 1), kContents); -} - -// Tests that calling rename with file path ending with . or .. causes EBUSY. -TEST(RenameTest, PathEndingWithDots) { - TempPath root_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath dir1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); - TempPath dir2 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path())); - - // Try to move dir1 into dir2 but mess up the paths. - auto dir1Dot = JoinPath(dir1.path(), "."); - auto dir2Dot = JoinPath(dir2.path(), "."); - auto dir1DotDot = JoinPath(dir1.path(), ".."); - auto dir2DotDot = JoinPath(dir2.path(), ".."); - ASSERT_THAT(rename(dir1.path().c_str(), dir2Dot.c_str()), - SyscallFailsWithErrno(EBUSY)); - ASSERT_THAT(rename(dir1.path().c_str(), dir2DotDot.c_str()), - SyscallFailsWithErrno(EBUSY)); - ASSERT_THAT(rename(dir1Dot.c_str(), dir2.path().c_str()), - SyscallFailsWithErrno(EBUSY)); - ASSERT_THAT(rename(dir1DotDot.c_str(), dir2.path().c_str()), - SyscallFailsWithErrno(EBUSY)); -} - -// Calling rename with file path ending with . or .. causes EBUSY in sysfs. -TEST(RenameTest, SysfsPathEndingWithDots) { - // If a non-root user tries to rename inside /sys then we get EPERM. - SKIP_IF(geteuid() != 0); - ASSERT_THAT(rename("/sys/devices/system/cpu/online", "/sys/."), - SyscallFailsWithErrno(EBUSY)); - ASSERT_THAT(rename("/sys/devices/system/cpu/online", "/sys/.."), - SyscallFailsWithErrno(EBUSY)); -} - -TEST(RenameTest, SysfsFileToSelf) { - // If a non-root user tries to rename inside /sys then we get EPERM. - SKIP_IF(geteuid() != 0); - std::string const path = "/sys/devices/system/cpu/online"; - EXPECT_THAT(rename(path.c_str(), path.c_str()), SyscallSucceeds()); -} - -TEST(RenameTest, SysfsDirectoryToSelf) { - // If a non-root user tries to rename inside /sys then we get EPERM. - SKIP_IF(geteuid() != 0); - std::string const path = "/sys/devices"; - EXPECT_THAT(rename(path.c_str(), path.c_str()), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/rlimits.cc b/test/syscalls/linux/rlimits.cc deleted file mode 100644 index 860f0f688..000000000 --- a/test/syscalls/linux/rlimits.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/resource.h> -#include <sys/time.h> - -#include "test/util/capability_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(RlimitTest, SetRlimitHigher) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))); - - struct rlimit rl = {}; - EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); - - // Lower the rlimit first, as it may be equal to /proc/sys/fs/nr_open, in - // which case even users with CAP_SYS_RESOURCE can't raise it. - rl.rlim_cur--; - rl.rlim_max--; - ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); - - rl.rlim_max++; - EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); -} - -TEST(RlimitTest, UnprivilegedSetRlimit) { - // Drop privileges if necessary. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_RESOURCE, false)); - } - - struct rlimit rl = {}; - rl.rlim_cur = 1000; - rl.rlim_max = 20000; - EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); - - struct rlimit rl2 = {}; - EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl2), SyscallSucceeds()); - EXPECT_EQ(rl.rlim_cur, rl2.rlim_cur); - EXPECT_EQ(rl.rlim_max, rl2.rlim_max); - - rl.rlim_max = 100000; - EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM)); -} - -TEST(RlimitTest, SetSoftRlimitAboveHard) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))); - - struct rlimit rl = {}; - EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds()); - - rl.rlim_cur = rl.rlim_max + 1; - EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/rseq.cc b/test/syscalls/linux/rseq.cc deleted file mode 100644 index 94f9154a0..000000000 --- a/test/syscalls/linux/rseq.cc +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/rseq/test.h" -#include "test/syscalls/linux/rseq/uapi.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::AnyOf; -using ::testing::Eq; - -// Syscall test for rseq (restartable sequences). -// -// We must be very careful about how these tests are written. Each thread may -// only have one struct rseq registration, which may be done automatically at -// thread start (as of 2019-11-13, glibc does *not* support rseq and thus does -// not do so, but other libraries do). -// -// Testing of rseq is thus done primarily in a child process with no -// registration. This means exec'ing a nostdlib binary, as rseq registration can -// only be cleared by execve (or knowing the old rseq address), and glibc (based -// on the current unmerged patches) register rseq before calling main()). - -int RSeq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) { - return syscall(kRseqSyscall, rseq, rseq_len, flags, sig); -} - -// Returns true if this kernel supports the rseq syscall. -PosixErrorOr<bool> RSeqSupported() { - // We have to be careful here, there are three possible cases: - // - // 1. rseq is not supported -> ENOSYS - // 2. rseq is supported and not registered -> success, but we should - // unregister. - // 3. rseq is supported and registered -> EINVAL (most likely). - - // The only validation done on new registrations is that rseq is aligned and - // writable. - rseq rseq = {}; - int ret = RSeq(&rseq, sizeof(rseq), 0, 0); - if (ret == 0) { - // Successfully registered, rseq is supported. Unregister. - ret = RSeq(&rseq, sizeof(rseq), kRseqFlagUnregister, 0); - if (ret != 0) { - return PosixError(errno); - } - return true; - } - - switch (errno) { - case ENOSYS: - // Not supported. - return false; - case EINVAL: - // Supported, but already registered. EINVAL returned because we provided - // a different address. - return true; - default: - // Unknown error. - return PosixError(errno); - } -} - -constexpr char kRseqBinary[] = "test/syscalls/linux/rseq/rseq"; - -void RunChildTest(std::string test_case, int want_status) { - std::string path = RunfilePath(kRseqBinary); - - pid_t child_pid = -1; - int execve_errno = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(path, {path, test_case}, {}, &child_pid, &execve_errno)); - - ASSERT_GT(child_pid, 0); - ASSERT_EQ(execve_errno, 0); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - ASSERT_THAT(status, AnyOf(Eq(want_status), Eq(128 + want_status))); -} - -// Test that rseq must be aligned. -TEST(RseqTest, Unaligned) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestUnaligned, 0); -} - -// Sanity test that registration works. -TEST(RseqTest, Register) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestRegister, 0); -} - -// Registration can't be done twice. -TEST(RseqTest, DoubleRegister) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestDoubleRegister, 0); -} - -// Registration can be done again after unregister. -TEST(RseqTest, RegisterUnregister) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestRegisterUnregister, 0); -} - -// The pointer to rseq must match on register/unregister. -TEST(RseqTest, UnregisterDifferentPtr) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestUnregisterDifferentPtr, 0); -} - -// The signature must match on register/unregister. -TEST(RseqTest, UnregisterDifferentSignature) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestUnregisterDifferentSignature, 0); -} - -// The CPU ID is initialized. -TEST(RseqTest, CPU) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestCPU, 0); -} - -// Critical section is eventually aborted. -TEST(RseqTest, Abort) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestAbort, 0); -} - -// Abort may be before the critical section. -TEST(RseqTest, AbortBefore) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestAbortBefore, 0); -} - -// Signature must match. -TEST(RseqTest, AbortSignature) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestAbortSignature, SIGSEGV); -} - -// Abort must not be in the critical section. -TEST(RseqTest, AbortPreCommit) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestAbortPreCommit, SIGSEGV); -} - -// rseq.rseq_cs is cleared on abort. -TEST(RseqTest, AbortClearsCS) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestAbortClearsCS, 0); -} - -// rseq.rseq_cs is cleared on abort outside of critical section. -TEST(RseqTest, InvalidAbortClearsCS) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(RSeqSupported())); - - RunChildTest(kRseqTestInvalidAbortClearsCS, 0); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/rseq/BUILD b/test/syscalls/linux/rseq/BUILD deleted file mode 100644 index 853258b04..000000000 --- a/test/syscalls/linux/rseq/BUILD +++ /dev/null @@ -1,61 +0,0 @@ -# This package contains a standalone rseq test binary. This binary must not -# depend on libc, which might use rseq itself. - -load("//tools:defs.bzl", "cc_flags_supplier", "cc_library", "cc_toolchain", "select_arch") - -package(licenses = ["notice"]) - -genrule( - name = "rseq_binary", - srcs = [ - "critical.h", - "critical_amd64.S", - "critical_arm64.S", - "rseq.cc", - "syscalls.h", - "start_amd64.S", - "start_arm64.S", - "test.h", - "types.h", - "uapi.h", - ], - outs = ["rseq"], - cmd = "$(CC) " + - "$(CC_FLAGS) " + - "-I. " + - "-Wall " + - "-Werror " + - "-O2 " + - "-std=c++17 " + - "-static " + - "-nostdlib " + - "-ffreestanding " + - "-o " + - "$(location rseq) " + - select_arch( - amd64 = "$(location critical_amd64.S) $(location start_amd64.S) ", - arm64 = "$(location critical_arm64.S) $(location start_arm64.S) ", - no_match_error = "unsupported architecture", - ) + - "$(location rseq.cc)", - toolchains = [ - cc_toolchain, - ":no_pie_cc_flags", - ], - visibility = ["//:sandbox"], -) - -cc_flags_supplier( - name = "no_pie_cc_flags", - features = ["-pie"], -) - -cc_library( - name = "lib", - testonly = 1, - hdrs = [ - "test.h", - "uapi.h", - ], - visibility = ["//:sandbox"], -) diff --git a/test/syscalls/linux/rseq/critical.h b/test/syscalls/linux/rseq/critical.h deleted file mode 100644 index ac987a25e..000000000 --- a/test/syscalls/linux/rseq/critical.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_RSEQ_CRITICAL_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_CRITICAL_H_ - -#include "test/syscalls/linux/rseq/types.h" -#include "test/syscalls/linux/rseq/uapi.h" - -constexpr uint32_t kRseqSignature = 0x90909090; - -extern "C" { - -extern void rseq_loop(struct rseq* r, struct rseq_cs* cs); -extern void* rseq_loop_early_abort; -extern void* rseq_loop_start; -extern void* rseq_loop_pre_commit; -extern void* rseq_loop_post_commit; -extern void* rseq_loop_abort; - -extern int rseq_getpid(struct rseq* r, struct rseq_cs* cs); -extern void* rseq_getpid_start; -extern void* rseq_getpid_post_commit; -extern void* rseq_getpid_abort; - -} // extern "C" - -#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_CRITICAL_H_ diff --git a/test/syscalls/linux/rseq/critical_amd64.S b/test/syscalls/linux/rseq/critical_amd64.S deleted file mode 100644 index 8c0687e6d..000000000 --- a/test/syscalls/linux/rseq/critical_amd64.S +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2019 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. - -// Restartable sequences critical sections. - -// Loops continuously until aborted. -// -// void rseq_loop(struct rseq* r, struct rseq_cs* cs) - - .text - .globl rseq_loop - .type rseq_loop, @function - -rseq_loop: - jmp begin - - // Abort block before the critical section. - // Abort signature is 4 nops for simplicity. - .byte 0x90, 0x90, 0x90, 0x90 - .globl rseq_loop_early_abort -rseq_loop_early_abort: - ret - -begin: - // r->rseq_cs = cs - movq %rsi, 8(%rdi) - - // N.B. rseq_cs will be cleared by any preempt, even outside the critical - // section. Thus it must be set in or immediately before the critical section - // to ensure it is not cleared before the section begins. - .globl rseq_loop_start -rseq_loop_start: - jmp rseq_loop_start - - // "Pre-commit": extra instructions inside the critical section. These are - // used as the abort point in TestAbortPreCommit, which is not valid. - .globl rseq_loop_pre_commit -rseq_loop_pre_commit: - // Extra abort signature + nop for TestAbortPostCommit. - .byte 0x90, 0x90, 0x90, 0x90 - nop - - // "Post-commit": never reached in this case. - .globl rseq_loop_post_commit -rseq_loop_post_commit: - - // Abort signature is 4 nops for simplicity. - .byte 0x90, 0x90, 0x90, 0x90 - - .globl rseq_loop_abort -rseq_loop_abort: - ret - - .size rseq_loop,.-rseq_loop - .section .note.GNU-stack,"",@progbits diff --git a/test/syscalls/linux/rseq/critical_arm64.S b/test/syscalls/linux/rseq/critical_arm64.S deleted file mode 100644 index bfe7e8307..000000000 --- a/test/syscalls/linux/rseq/critical_arm64.S +++ /dev/null @@ -1,66 +0,0 @@ -// 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. - -// Restartable sequences critical sections. - -// Loops continuously until aborted. -// -// void rseq_loop(struct rseq* r, struct rseq_cs* cs) - - .text - .globl rseq_loop - .type rseq_loop, @function - -rseq_loop: - b begin - - // Abort block before the critical section. - // Abort signature. - .byte 0x90, 0x90, 0x90, 0x90 - .globl rseq_loop_early_abort -rseq_loop_early_abort: - ret - -begin: - // r->rseq_cs = cs - str x1, [x0, #8] - - // N.B. rseq_cs will be cleared by any preempt, even outside the critical - // section. Thus it must be set in or immediately before the critical section - // to ensure it is not cleared before the section begins. - .globl rseq_loop_start -rseq_loop_start: - b rseq_loop_start - - // "Pre-commit": extra instructions inside the critical section. These are - // used as the abort point in TestAbortPreCommit, which is not valid. - .globl rseq_loop_pre_commit -rseq_loop_pre_commit: - // Extra abort signature + nop for TestAbortPostCommit. - .byte 0x90, 0x90, 0x90, 0x90 - nop - - // "Post-commit": never reached in this case. - .globl rseq_loop_post_commit -rseq_loop_post_commit: - - // Abort signature. - .byte 0x90, 0x90, 0x90, 0x90 - - .globl rseq_loop_abort -rseq_loop_abort: - ret - - .size rseq_loop,.-rseq_loop - .section .note.GNU-stack,"",@progbits diff --git a/test/syscalls/linux/rseq/rseq.cc b/test/syscalls/linux/rseq/rseq.cc deleted file mode 100644 index 6f5d38bba..000000000 --- a/test/syscalls/linux/rseq/rseq.cc +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright 2019 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/syscalls/linux/rseq/critical.h" -#include "test/syscalls/linux/rseq/syscalls.h" -#include "test/syscalls/linux/rseq/test.h" -#include "test/syscalls/linux/rseq/types.h" -#include "test/syscalls/linux/rseq/uapi.h" - -namespace gvisor { -namespace testing { - -extern "C" int main(int argc, char** argv, char** envp); - -// Standalone initialization before calling main(). -extern "C" void __init(uintptr_t* sp) { - int argc = sp[0]; - char** argv = reinterpret_cast<char**>(&sp[1]); - char** envp = &argv[argc + 1]; - - // Call main() and exit. - sys_exit_group(main(argc, argv, envp)); - - // sys_exit_group does not return -} - -int strcmp(const char* s1, const char* s2) { - const unsigned char* p1 = reinterpret_cast<const unsigned char*>(s1); - const unsigned char* p2 = reinterpret_cast<const unsigned char*>(s2); - - while (*p1 == *p2) { - if (!*p1) { - return 0; - } - ++p1; - ++p2; - } - return static_cast<int>(*p1) - static_cast<int>(*p2); -} - -int sys_rseq(struct rseq* rseq, uint32_t rseq_len, int flags, uint32_t sig) { - return raw_syscall(kRseqSyscall, rseq, rseq_len, flags, sig); -} - -// Test that rseq must be aligned. -int TestUnaligned() { - constexpr uintptr_t kRequiredAlignment = alignof(rseq); - - char buf[2 * kRequiredAlignment] = {}; - uintptr_t ptr = reinterpret_cast<uintptr_t>(&buf[0]); - if ((ptr & (kRequiredAlignment - 1)) == 0) { - // buf is already aligned. Misalign it. - ptr++; - } - - int ret = sys_rseq(reinterpret_cast<rseq*>(ptr), sizeof(rseq), 0, 0); - if (sys_errno(ret) != EINVAL) { - return 1; - } - return 0; -} - -// Sanity test that registration works. -int TestRegister() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != 0) { - return 1; - } - return 0; -} - -// Registration can't be done twice. -int TestDoubleRegister() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != 0) { - return 1; - } - - ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != EBUSY) { - return 1; - } - - return 0; -} - -// Registration can be done again after unregister. -int TestRegisterUnregister() { - struct rseq r = {}; - - int ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != 0) { - return 1; - } - - ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, 0); - if (sys_errno(ret) != 0) { - return 1; - } - - ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != 0) { - return 1; - } - - return 0; -} - -// The pointer to rseq must match on register/unregister. -int TestUnregisterDifferentPtr() { - struct rseq r = {}; - - int ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq r2 = {}; - - ret = sys_rseq(&r2, sizeof(r2), kRseqFlagUnregister, 0); - if (sys_errno(ret) != EINVAL) { - return 1; - } - - return 0; -} - -// The signature must match on register/unregister. -int TestUnregisterDifferentSignature() { - constexpr int kSignature = 0; - - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kSignature); - if (sys_errno(ret) != 0) { - return 1; - } - - ret = sys_rseq(&r, sizeof(r), kRseqFlagUnregister, kSignature + 1); - if (sys_errno(ret) != EPERM) { - return 1; - } - - return 0; -} - -// The CPU ID is initialized. -int TestCPU() { - struct rseq r = {}; - r.cpu_id = kRseqCPUIDUninitialized; - - int ret = sys_rseq(&r, sizeof(r), 0, 0); - if (sys_errno(ret) != 0) { - return 1; - } - - if (__atomic_load_n(&r.cpu_id, __ATOMIC_RELAXED) < 0) { - return 1; - } - if (__atomic_load_n(&r.cpu_id_start, __ATOMIC_RELAXED) < 0) { - return 1; - } - - return 0; -} - -// Critical section is eventually aborted. -int TestAbort() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq_cs cs = {}; - cs.version = 0; - cs.flags = 0; - cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - - reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); - - // Loops until abort. If this returns then abort occurred. - rseq_loop(&r, &cs); - - return 0; -} - -// Abort may be before the critical section. -int TestAbortBefore() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq_cs cs = {}; - cs.version = 0; - cs.flags = 0; - cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - - reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_early_abort); - - // Loops until abort. If this returns then abort occurred. - rseq_loop(&r, &cs); - - return 0; -} - -// Signature must match. -int TestAbortSignature() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq_cs cs = {}; - cs.version = 0; - cs.flags = 0; - cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - - reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); - - // Loops until abort. This should SIGSEGV on abort. - rseq_loop(&r, &cs); - - return 1; -} - -// Abort must not be in the critical section. -int TestAbortPreCommit() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature + 1); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq_cs cs = {}; - cs.version = 0; - cs.flags = 0; - cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - - reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_pre_commit); - - // Loops until abort. This should SIGSEGV on abort. - rseq_loop(&r, &cs); - - return 1; -} - -// rseq.rseq_cs is cleared on abort. -int TestAbortClearsCS() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq_cs cs = {}; - cs.version = 0; - cs.flags = 0; - cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - - reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); - - // Loops until abort. If this returns then abort occurred. - rseq_loop(&r, &cs); - - if (__atomic_load_n(&r.rseq_cs, __ATOMIC_RELAXED)) { - return 1; - } - - return 0; -} - -// rseq.rseq_cs is cleared on abort outside of critical section. -int TestInvalidAbortClearsCS() { - struct rseq r = {}; - int ret = sys_rseq(&r, sizeof(r), 0, kRseqSignature); - if (sys_errno(ret) != 0) { - return 1; - } - - struct rseq_cs cs = {}; - cs.version = 0; - cs.flags = 0; - cs.start_ip = reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.post_commit_offset = reinterpret_cast<uint64_t>(&rseq_loop_post_commit) - - reinterpret_cast<uint64_t>(&rseq_loop_start); - cs.abort_ip = reinterpret_cast<uint64_t>(&rseq_loop_abort); - - __atomic_store_n(&r.rseq_cs, &cs, __ATOMIC_RELAXED); - - // When the next abort condition occurs, the kernel will clear cs once it - // determines we aren't in the critical section. - while (1) { - if (!__atomic_load_n(&r.rseq_cs, __ATOMIC_RELAXED)) { - break; - } - } - - return 0; -} - -// Exit codes: -// 0 - Pass -// 1 - Fail -// 2 - Missing argument -// 3 - Unknown test case -extern "C" int main(int argc, char** argv, char** envp) { - if (argc != 2) { - // Usage: rseq <test case> - return 2; - } - - if (strcmp(argv[1], kRseqTestUnaligned) == 0) { - return TestUnaligned(); - } - if (strcmp(argv[1], kRseqTestRegister) == 0) { - return TestRegister(); - } - if (strcmp(argv[1], kRseqTestDoubleRegister) == 0) { - return TestDoubleRegister(); - } - if (strcmp(argv[1], kRseqTestRegisterUnregister) == 0) { - return TestRegisterUnregister(); - } - if (strcmp(argv[1], kRseqTestUnregisterDifferentPtr) == 0) { - return TestUnregisterDifferentPtr(); - } - if (strcmp(argv[1], kRseqTestUnregisterDifferentSignature) == 0) { - return TestUnregisterDifferentSignature(); - } - if (strcmp(argv[1], kRseqTestCPU) == 0) { - return TestCPU(); - } - if (strcmp(argv[1], kRseqTestAbort) == 0) { - return TestAbort(); - } - if (strcmp(argv[1], kRseqTestAbortBefore) == 0) { - return TestAbortBefore(); - } - if (strcmp(argv[1], kRseqTestAbortSignature) == 0) { - return TestAbortSignature(); - } - if (strcmp(argv[1], kRseqTestAbortPreCommit) == 0) { - return TestAbortPreCommit(); - } - if (strcmp(argv[1], kRseqTestAbortClearsCS) == 0) { - return TestAbortClearsCS(); - } - if (strcmp(argv[1], kRseqTestInvalidAbortClearsCS) == 0) { - return TestInvalidAbortClearsCS(); - } - - return 3; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/rseq/start_amd64.S b/test/syscalls/linux/rseq/start_amd64.S deleted file mode 100644 index b9611b276..000000000 --- a/test/syscalls/linux/rseq/start_amd64.S +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019 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. - - - .text - .align 4 - .type _start,@function - .globl _start - -_start: - movq %rsp,%rdi - call __init - hlt - - .size _start,.-_start - .section .note.GNU-stack,"",@progbits - - .text - .globl raw_syscall - .type raw_syscall, @function - -raw_syscall: - mov %rdi,%rax // syscall # - mov %rsi,%rdi // arg0 - mov %rdx,%rsi // arg1 - mov %rcx,%rdx // arg2 - mov %r8,%r10 // arg3 (goes in r10 instead of rcx for system calls) - mov %r9,%r8 // arg4 - mov 0x8(%rsp),%r9 // arg5 - syscall - ret - - .size raw_syscall,.-raw_syscall - .section .note.GNU-stack,"",@progbits diff --git a/test/syscalls/linux/rseq/start_arm64.S b/test/syscalls/linux/rseq/start_arm64.S deleted file mode 100644 index 693c1c6eb..000000000 --- a/test/syscalls/linux/rseq/start_arm64.S +++ /dev/null @@ -1,45 +0,0 @@ -// 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. - - - .text - .align 4 - .type _start,@function - .globl _start - -_start: - mov x29, sp - bl __init - wfi - - .size _start,.-_start - .section .note.GNU-stack,"",@progbits - - .text - .globl raw_syscall - .type raw_syscall, @function - -raw_syscall: - mov x8,x0 // syscall # - mov x0,x1 // arg0 - mov x1,x2 // arg1 - mov x2,x3 // arg2 - mov x3,x4 // arg3 - mov x4,x5 // arg4 - mov x5,x6 // arg5 - svc #0 - ret - - .size raw_syscall,.-raw_syscall - .section .note.GNU-stack,"",@progbits diff --git a/test/syscalls/linux/rseq/syscalls.h b/test/syscalls/linux/rseq/syscalls.h deleted file mode 100644 index c4118e6c5..000000000 --- a/test/syscalls/linux/rseq/syscalls.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_RSEQ_SYSCALLS_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_SYSCALLS_H_ - -#include "test/syscalls/linux/rseq/types.h" - -// Syscall numbers. -#if defined(__x86_64__) -constexpr int kGetpid = 39; -constexpr int kExitGroup = 231; -#elif defined(__aarch64__) -constexpr int kGetpid = 172; -constexpr int kExitGroup = 94; -#else -#error "Unknown architecture" -#endif - -namespace gvisor { -namespace testing { - -// Standalone system call interfaces. -// Note that these are all "raw" system call interfaces which encode -// errors by setting the return value to a small negative number. -// Use sys_errno() to check system call return values for errors. - -// Maximum Linux error number. -constexpr int kMaxErrno = 4095; - -// Errno values. -#define EPERM 1 -#define EFAULT 14 -#define EBUSY 16 -#define EINVAL 22 - -// Get the error number from a raw system call return value. -// Returns a positive error number or 0 if there was no error. -static inline int sys_errno(uintptr_t rval) { - if (rval >= static_cast<uintptr_t>(-kMaxErrno)) { - return -static_cast<int>(rval); - } - return 0; -} - -extern "C" uintptr_t raw_syscall(int number, ...); - -static inline void sys_exit_group(int status) { - raw_syscall(kExitGroup, status); -} -static inline int sys_getpid() { - return static_cast<int>(raw_syscall(kGetpid)); -} - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_SYSCALLS_H_ diff --git a/test/syscalls/linux/rseq/test.h b/test/syscalls/linux/rseq/test.h deleted file mode 100644 index ff0dd6e48..000000000 --- a/test/syscalls/linux/rseq/test.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_RSEQ_TEST_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TEST_H_ - -namespace gvisor { -namespace testing { - -// Test cases supported by rseq binary. - -constexpr char kRseqTestUnaligned[] = "unaligned"; -constexpr char kRseqTestRegister[] = "register"; -constexpr char kRseqTestDoubleRegister[] = "double-register"; -constexpr char kRseqTestRegisterUnregister[] = "register-unregister"; -constexpr char kRseqTestUnregisterDifferentPtr[] = "unregister-different-ptr"; -constexpr char kRseqTestUnregisterDifferentSignature[] = - "unregister-different-signature"; -constexpr char kRseqTestCPU[] = "cpu"; -constexpr char kRseqTestAbort[] = "abort"; -constexpr char kRseqTestAbortBefore[] = "abort-before"; -constexpr char kRseqTestAbortSignature[] = "abort-signature"; -constexpr char kRseqTestAbortPreCommit[] = "abort-precommit"; -constexpr char kRseqTestAbortClearsCS[] = "abort-clears-cs"; -constexpr char kRseqTestInvalidAbortClearsCS[] = "invalid-abort-clears-cs"; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TEST_H_ diff --git a/test/syscalls/linux/rseq/types.h b/test/syscalls/linux/rseq/types.h deleted file mode 100644 index b6afe9817..000000000 --- a/test/syscalls/linux/rseq/types.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_RSEQ_TYPES_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TYPES_H_ - -using size_t = __SIZE_TYPE__; -using uintptr_t = __UINTPTR_TYPE__; - -using uint8_t = __UINT8_TYPE__; -using uint16_t = __UINT16_TYPE__; -using uint32_t = __UINT32_TYPE__; -using uint64_t = __UINT64_TYPE__; - -using int8_t = __INT8_TYPE__; -using int16_t = __INT16_TYPE__; -using int32_t = __INT32_TYPE__; -using int64_t = __INT64_TYPE__; - -#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_TYPES_H_ diff --git a/test/syscalls/linux/rseq/uapi.h b/test/syscalls/linux/rseq/uapi.h deleted file mode 100644 index d3e60d0a4..000000000 --- a/test/syscalls/linux/rseq/uapi.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_RSEQ_UAPI_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_RSEQ_UAPI_H_ - -#include <stdint.h> - -// User-kernel ABI for restartable sequences. - -// Syscall numbers. -#if defined(__x86_64__) -constexpr int kRseqSyscall = 334; -#elif defined(__aarch64__) -constexpr int kRseqSyscall = 293; -#else -#error "Unknown architecture" -#endif // __x86_64__ - -struct rseq_cs { - uint32_t version; - uint32_t flags; - uint64_t start_ip; - uint64_t post_commit_offset; - uint64_t abort_ip; -} __attribute__((aligned(4 * sizeof(uint64_t)))); - -// N.B. alignment is enforced by the kernel. -struct rseq { - uint32_t cpu_id_start; - uint32_t cpu_id; - struct rseq_cs* rseq_cs; - uint32_t flags; -} __attribute__((aligned(4 * sizeof(uint64_t)))); - -constexpr int kRseqFlagUnregister = 1 << 0; - -constexpr int kRseqCPUIDUninitialized = -1; - -#endif // GVISOR_TEST_SYSCALLS_LINUX_RSEQ_UAPI_H_ diff --git a/test/syscalls/linux/rtsignal.cc b/test/syscalls/linux/rtsignal.cc deleted file mode 100644 index ed27e2566..000000000 --- a/test/syscalls/linux/rtsignal.cc +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <cerrno> -#include <csignal> - -#include "gtest/gtest.h" -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// saved_info is set by the handler. -siginfo_t saved_info; - -// has_saved_info is set to true by the handler. -volatile bool has_saved_info; - -void SigHandler(int sig, siginfo_t* info, void* context) { - // Copy to the given info. - saved_info = *info; - has_saved_info = true; -} - -void ClearSavedInfo() { - // Clear the cached info. - memset(&saved_info, 0, sizeof(saved_info)); - has_saved_info = false; -} - -PosixErrorOr<Cleanup> SetupSignalHandler(int sig) { - struct sigaction sa; - sa.sa_sigaction = SigHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - return ScopedSigaction(sig, sa); -} - -class RtSignalTest : public ::testing::Test { - protected: - void SetUp() override { - action_cleanup_ = ASSERT_NO_ERRNO_AND_VALUE(SetupSignalHandler(SIGUSR1)); - mask_cleanup_ = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGUSR1)); - } - - void TearDown() override { ClearSavedInfo(); } - - private: - Cleanup action_cleanup_; - Cleanup mask_cleanup_; -}; - -static int rt_sigqueueinfo(pid_t tgid, int sig, siginfo_t* uinfo) { - int ret; - do { - // NOTE(b/25434735): rt_sigqueueinfo(2) could return EAGAIN for RT signals. - ret = syscall(SYS_rt_sigqueueinfo, tgid, sig, uinfo); - } while (ret == -1 && errno == EAGAIN); - return ret; -} - -TEST_F(RtSignalTest, InvalidTID) { - siginfo_t uinfo; - // Depending on the kernel version, these calls may fail with - // ESRCH (goobunutu machines) or EPERM (production machines). Thus, - // the test simply ensures that they do fail. - EXPECT_THAT(rt_sigqueueinfo(-1, SIGUSR1, &uinfo), SyscallFails()); - EXPECT_FALSE(has_saved_info); - EXPECT_THAT(rt_sigqueueinfo(0, SIGUSR1, &uinfo), SyscallFails()); - EXPECT_FALSE(has_saved_info); -} - -TEST_F(RtSignalTest, InvalidCodes) { - siginfo_t uinfo; - - // We need a child for the code checks to apply. If the process is delivering - // to itself, then it can use whatever codes it wants and they will go - // through. - pid_t child = fork(); - if (child == 0) { - _exit(1); - } - ASSERT_THAT(child, SyscallSucceeds()); - - // These are not allowed for child processes. - uinfo.si_code = 0; // SI_USER. - EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), - SyscallFailsWithErrno(EPERM)); - uinfo.si_code = 0x80; // SI_KERNEL. - EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), - SyscallFailsWithErrno(EPERM)); - uinfo.si_code = -6; // SI_TKILL. - EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), - SyscallFailsWithErrno(EPERM)); - uinfo.si_code = -1; // SI_QUEUE (allowed). - EXPECT_THAT(rt_sigqueueinfo(child, SIGUSR1, &uinfo), SyscallSucceeds()); - - // Join the child process. - EXPECT_THAT(waitpid(child, nullptr, 0), SyscallSucceeds()); -} - -TEST_F(RtSignalTest, ValueDelivered) { - siginfo_t uinfo; - uinfo.si_code = -1; // SI_QUEUE (allowed). - uinfo.si_errno = 0x1234; - - EXPECT_EQ(saved_info.si_errno, 0x0); - EXPECT_THAT(rt_sigqueueinfo(getpid(), SIGUSR1, &uinfo), SyscallSucceeds()); - EXPECT_TRUE(has_saved_info); - EXPECT_EQ(saved_info.si_errno, 0x1234); -} - -TEST_F(RtSignalTest, SignoMatch) { - auto action2_cleanup = ASSERT_NO_ERRNO_AND_VALUE(SetupSignalHandler(SIGUSR2)); - auto mask2_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGUSR2)); - - siginfo_t uinfo; - uinfo.si_code = -1; // SI_QUEUE (allowed). - - EXPECT_THAT(rt_sigqueueinfo(getpid(), SIGUSR1, &uinfo), SyscallSucceeds()); - EXPECT_TRUE(has_saved_info); - EXPECT_EQ(saved_info.si_signo, SIGUSR1); - - ClearSavedInfo(); - - EXPECT_THAT(rt_sigqueueinfo(getpid(), SIGUSR2, &uinfo), SyscallSucceeds()); - EXPECT_TRUE(has_saved_info); - EXPECT_EQ(saved_info.si_signo, SIGUSR2); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - // These tests depend on delivering SIGUSR1/2 to the main thread (so they can - // synchronously check has_saved_info). Block these so that any other threads - // created by TestInit will also have them blocked. - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGUSR1); - sigaddset(&set, SIGUSR2); - TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/sched.cc b/test/syscalls/linux/sched.cc deleted file mode 100644 index 735e99411..000000000 --- a/test/syscalls/linux/sched.cc +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sched.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// In linux, pid is limited to 29 bits because how futex is implemented. -constexpr int kImpossiblePID = (1 << 29) + 1; - -TEST(SchedGetparamTest, ReturnsZero) { - struct sched_param param; - EXPECT_THAT(sched_getparam(getpid(), ¶m), SyscallSucceeds()); - EXPECT_EQ(param.sched_priority, 0); - EXPECT_THAT(sched_getparam(/*pid=*/0, ¶m), SyscallSucceeds()); - EXPECT_EQ(param.sched_priority, 0); -} - -TEST(SchedGetparamTest, InvalidPIDReturnsEINVAL) { - struct sched_param param; - EXPECT_THAT(sched_getparam(/*pid=*/-1, ¶m), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SchedGetparamTest, ImpossiblePIDReturnsESRCH) { - struct sched_param param; - EXPECT_THAT(sched_getparam(kImpossiblePID, ¶m), - SyscallFailsWithErrno(ESRCH)); -} - -TEST(SchedGetparamTest, NullParamReturnsEINVAL) { - EXPECT_THAT(sched_getparam(0, nullptr), SyscallFailsWithErrno(EINVAL)); -} - -TEST(SchedGetschedulerTest, ReturnsSchedOther) { - EXPECT_THAT(sched_getscheduler(getpid()), - SyscallSucceedsWithValue(SCHED_OTHER)); - EXPECT_THAT(sched_getscheduler(/*pid=*/0), - SyscallSucceedsWithValue(SCHED_OTHER)); -} - -TEST(SchedGetschedulerTest, ReturnsEINVAL) { - EXPECT_THAT(sched_getscheduler(/*pid=*/-1), SyscallFailsWithErrno(EINVAL)); -} - -TEST(SchedGetschedulerTest, ReturnsESRCH) { - EXPECT_THAT(sched_getscheduler(kImpossiblePID), SyscallFailsWithErrno(ESRCH)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sched_yield.cc b/test/syscalls/linux/sched_yield.cc deleted file mode 100644 index 5d24f5b58..000000000 --- a/test/syscalls/linux/sched_yield.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sched.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SchedYieldTest, Success) { - EXPECT_THAT(sched_yield(), SyscallSucceeds()); - EXPECT_THAT(sched_yield(), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/seccomp.cc b/test/syscalls/linux/seccomp.cc deleted file mode 100644 index ce88d90dd..000000000 --- a/test/syscalls/linux/seccomp.cc +++ /dev/null @@ -1,425 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <linux/audit.h> -#include <linux/filter.h> -#include <linux/seccomp.h> -#include <pthread.h> -#include <sched.h> -#include <signal.h> -#include <string.h> -#include <sys/prctl.h> -#include <sys/syscall.h> -#include <time.h> -#include <ucontext.h> -#include <unistd.h> - -#include <atomic> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/proc_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -#ifndef SYS_SECCOMP -#define SYS_SECCOMP 1 -#endif - -namespace gvisor { -namespace testing { - -namespace { - -// A syscall not implemented by Linux that we don't expect to be called. -#ifdef __x86_64__ -constexpr uint32_t kFilteredSyscall = SYS_vserver; -#elif __aarch64__ -// Use the last of arch_specific_syscalls which are not implemented on arm64. -constexpr uint32_t kFilteredSyscall = __NR_arch_specific_syscall + 15; -#endif - -// Applies a seccomp-bpf filter that returns `filtered_result` for -// `sysno` and allows all other syscalls. Async-signal-safe. -void ApplySeccompFilter(uint32_t sysno, uint32_t filtered_result, - uint32_t flags = 0) { - // "Prior to [PR_SET_SECCOMP], the task must call prctl(PR_SET_NO_NEW_PRIVS, - // 1) or run with CAP_SYS_ADMIN privileges in its namespace." - - // Documentation/prctl/seccomp_filter.txt - // - // prctl(PR_SET_NO_NEW_PRIVS, 1) may be called repeatedly; calls after the - // first are no-ops. - TEST_PCHECK(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == 0); - MaybeSave(); - - struct sock_filter filter[] = { - // A = seccomp_data.arch - BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 4), -#if defined(__x86_64__) - // if (A != AUDIT_ARCH_X86_64) goto kill - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_X86_64, 0, 4), -#elif defined(__aarch64__) - // if (A != AUDIT_ARCH_AARCH64) goto kill - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, AUDIT_ARCH_AARCH64, 0, 4), -#else -#error "Unknown architecture" -#endif - // A = seccomp_data.nr - BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0), - // if (A != sysno) goto allow - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, sysno, 0, 1), - // return filtered_result - BPF_STMT(BPF_RET | BPF_K, filtered_result), - // allow: return SECCOMP_RET_ALLOW - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - // kill: return SECCOMP_RET_KILL - BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL), - }; - struct sock_fprog prog; - prog.len = ABSL_ARRAYSIZE(filter); - prog.filter = filter; - if (flags) { - TEST_CHECK(syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, flags, &prog) == - 0); - } else { - TEST_PCHECK(prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0) == 0); - } - MaybeSave(); -} - -// Wrapper for sigaction. Async-signal-safe. -void RegisterSignalHandler(int signum, - void (*handler)(int, siginfo_t*, void*)) { - struct sigaction sa = {}; - sa.sa_sigaction = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - TEST_PCHECK(sigaction(signum, &sa, nullptr) == 0); - MaybeSave(); -} - -// All of the following tests execute in a subprocess to ensure that each test -// is run in a separate process. This avoids cross-contamination of seccomp -// state between tests, and is necessary to ensure that test processes killed -// by SECCOMP_RET_KILL are single-threaded (since SECCOMP_RET_KILL only kills -// the offending thread, not the whole thread group). - -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); }); - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); - syscall(kFilteredSyscall); - TEST_CHECK_MSG(false, "Survived invocation of test syscall"); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS) - << "status " << status; -} - -TEST(SeccompTest, RetKillOnlyKillsOneThread) { - Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - - 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); }); - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); - // Pass CLONE_VFORK to block the original thread in the child process until - // the clone thread exits with SIGSYS. - // - // N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's - // x86_64 implementation is safe. See glibc - // sysdeps/unix/sysv/linux/x86_64/clone.S. - clone( - +[](void* arg) { - syscall(kFilteredSyscall); // should kill the thread - _exit(1); // should be unreachable - return 2; // should be very unreachable, shut up the compiler - }, - stack.endptr(), - CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | - CLONE_VFORK, - nullptr); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -TEST(SeccompTest, RetTrapCausesSIGSYS) { - pid_t const pid = fork(); - if (pid == 0) { - constexpr uint16_t kTrapValue = 0xdead; - RegisterSignalHandler( - SIGSYS, +[](int signo, siginfo_t* info, void* ucv) { - ucontext_t* uc = static_cast<ucontext_t*>(ucv); - // This is a signal handler, so we must stay async-signal-safe. - TEST_CHECK(info->si_signo == SIGSYS); - TEST_CHECK(info->si_code == SYS_SECCOMP); - TEST_CHECK(info->si_errno == kTrapValue); - TEST_CHECK(info->si_call_addr != nullptr); - TEST_CHECK(info->si_syscall == kFilteredSyscall); -#if defined(__x86_64__) - TEST_CHECK(info->si_arch == AUDIT_ARCH_X86_64); - TEST_CHECK(uc->uc_mcontext.gregs[REG_RAX] == kFilteredSyscall); -#elif defined(__aarch64__) - TEST_CHECK(info->si_arch == AUDIT_ARCH_AARCH64); - TEST_CHECK(uc->uc_mcontext.regs[8] == kFilteredSyscall); -#endif // defined(__x86_64__) - _exit(0); - }); - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRAP | kTrapValue); - syscall(kFilteredSyscall); - TEST_CHECK_MSG(false, "Survived invocation of test syscall"); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -#ifdef __x86_64__ - -constexpr uint64_t kVsyscallTimeEntry = 0xffffffffff600400; - -time_t vsyscall_time(time_t* t) { - return reinterpret_cast<time_t (*)(time_t*)>(kVsyscallTimeEntry)(t); -} - -TEST(SeccompTest, SeccompAppliesToVsyscall) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); - - pid_t const pid = fork(); - if (pid == 0) { - constexpr uint16_t kTrapValue = 0xdead; - RegisterSignalHandler( - SIGSYS, +[](int signo, siginfo_t* info, void* ucv) { - ucontext_t* uc = static_cast<ucontext_t*>(ucv); - // This is a signal handler, so we must stay async-signal-safe. - TEST_CHECK(info->si_signo == SIGSYS); - TEST_CHECK(info->si_code == SYS_SECCOMP); - TEST_CHECK(info->si_errno == kTrapValue); - TEST_CHECK(info->si_call_addr != nullptr); - TEST_CHECK(info->si_syscall == SYS_time); - TEST_CHECK(info->si_arch == AUDIT_ARCH_X86_64); - TEST_CHECK(uc->uc_mcontext.gregs[REG_RAX] == SYS_time); - _exit(0); - }); - ApplySeccompFilter(SYS_time, SECCOMP_RET_TRAP | kTrapValue); - vsyscall_time(nullptr); // Should result in death. - TEST_CHECK_MSG(false, "Survived invocation of test syscall"); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -TEST(SeccompTest, RetKillVsyscallCausesDeathBySIGSYS) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); - - 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); }); - ApplySeccompFilter(SYS_time, SECCOMP_RET_KILL); - vsyscall_time(nullptr); // Should result in death. - TEST_CHECK_MSG(false, "Survived invocation of test syscall"); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS) - << "status " << status; -} - -#endif // defined(__x86_64__) - -TEST(SeccompTest, RetTraceWithoutPtracerReturnsENOSYS) { - pid_t const pid = fork(); - if (pid == 0) { - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRACE); - TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOSYS); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -TEST(SeccompTest, RetErrnoReturnsErrno) { - pid_t const pid = fork(); - if (pid == 0) { - // ENOTNAM: "Not a XENIX named type file" - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM); - TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOTNAM); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -TEST(SeccompTest, RetAllowAllowsSyscall) { - pid_t const pid = fork(); - if (pid == 0) { - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ALLOW); - TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOSYS); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -// This test will validate that TSYNC will apply to all threads. -TEST(SeccompTest, TsyncAppliesToAllThreads) { - Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - - // We don't want to apply this policy to other test runner threads, so fork. - const pid_t pid = fork(); - - if (pid == 0) { - // First check that we receive a ENOSYS before the policy is applied. - TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOSYS); - - // N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's - // x86_64 implementation is safe. See glibc - // sysdeps/unix/sysv/linux/x86_64/clone.S. - clone( - +[](void* arg) { - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM, - SECCOMP_FILTER_FLAG_TSYNC); - return 0; - }, - stack.endptr(), - CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | - CLONE_VFORK, - nullptr); - - // Because we're using CLONE_VFORK this thread will be blocked until - // the second thread has released resources to our virtual memory, since - // we're not execing that will happen on _exit. - - // Now verify that the policy applied to this thread too. - TEST_CHECK(syscall(kFilteredSyscall) == -1 && errno == ENOTNAM); - _exit(0); - } - - ASSERT_THAT(pid, SyscallSucceeds()); - int status = 0; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -// This test will validate that seccomp(2) rejects unsupported flags. -TEST(SeccompTest, SeccompRejectsUnknownFlags) { - constexpr uint32_t kInvalidFlag = 123; - ASSERT_THAT( - syscall(__NR_seccomp, SECCOMP_SET_MODE_FILTER, kInvalidFlag, nullptr), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SeccompTest, LeastPermissiveFilterReturnValueApplies) { - // This is RetKillCausesDeathBySIGSYS, plus extra filters before and after the - // 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); }); - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRACE); - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM); - syscall(kFilteredSyscall); - TEST_CHECK_MSG(false, "Survived invocation of test syscall"); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS) - << "status " << status; -} - -// Passed as argv[1] to cause the test binary to invoke kFilteredSyscall and -// exit. Not a real flag since flag parsing happens during initialization, -// which may create threads. -constexpr char kInvokeFilteredSyscallFlag[] = "--seccomp_test_child"; - -TEST(SeccompTest, FiltersPreservedAcrossForkAndExecve) { - ExecveArray const grandchild_argv( - {"/proc/self/exe", kInvokeFilteredSyscallFlag}); - - pid_t const pid = fork(); - if (pid == 0) { - ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL); - pid_t const grandchild_pid = fork(); - if (grandchild_pid == 0) { - execve(grandchild_argv.get()[0], grandchild_argv.get(), - /* envp = */ nullptr); - TEST_PCHECK_MSG(false, "execve failed"); - } - int status; - TEST_PCHECK(waitpid(grandchild_pid, &status, 0) == grandchild_pid); - TEST_CHECK(WIFSIGNALED(status) && WTERMSIG(status) == SIGSYS); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status " << status; -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - if (argc >= 2 && - strcmp(argv[1], gvisor::testing::kInvokeFilteredSyscallFlag) == 0) { - syscall(gvisor::testing::kFilteredSyscall); - exit(0); - } - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/select.cc b/test/syscalls/linux/select.cc deleted file mode 100644 index be2364fb8..000000000 --- a/test/syscalls/linux/select.cc +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/resource.h> -#include <sys/select.h> -#include <sys/time.h> - -#include <climits> -#include <csignal> -#include <cstdio> - -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/base_poll_test.h" -#include "test/util/file_descriptor.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/rlimit_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -class SelectTest : public BasePollTest { - protected: - void SetUp() override { BasePollTest::SetUp(); } - void TearDown() override { BasePollTest::TearDown(); } -}; - -// See that when there are no FD sets, select behaves like sleep. -TEST_F(SelectTest, NullFds) { - struct timeval timeout = absl::ToTimeval(absl::Milliseconds(10)); - ASSERT_THAT(select(0, nullptr, nullptr, nullptr, &timeout), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_usec, 0); - - timeout = absl::ToTimeval(absl::Milliseconds(10)); - ASSERT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), - SyscallSucceeds()); - EXPECT_EQ(timeout.tv_sec, 0); - EXPECT_EQ(timeout.tv_usec, 0); -} - -TEST_F(SelectTest, NegativeNfds) { - EXPECT_THAT(select(-1, nullptr, nullptr, nullptr, nullptr), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(select(-100000, nullptr, nullptr, nullptr, nullptr), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(select(INT_MIN, nullptr, nullptr, nullptr, nullptr), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(SelectTest, ClosedFds) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); - - // We can't rely on a file descriptor being closed in a multi threaded - // application so fork to get a clean process. - EXPECT_THAT(InForkedProcess([&] { - int fd_num = fd.get(); - fd.reset(); - - fd_set read_set; - FD_ZERO(&read_set); - FD_SET(fd_num, &read_set); - - struct timeval timeout = - absl::ToTimeval(absl::Milliseconds(10)); - TEST_PCHECK(select(fd_num + 1, &read_set, nullptr, nullptr, - &timeout) != 0); - TEST_PCHECK(errno == EBADF); - }), - IsPosixErrorOkAndHolds(0)); -} - -TEST_F(SelectTest, ZeroTimeout) { - struct timeval timeout = {}; - EXPECT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), - SyscallSucceeds()); - // Ignore timeout as its value is now undefined. -} - -// If random S/R interrupts the select, SIGALRM may be delivered before select -// restarts, causing the select to hang forever. -TEST_F(SelectTest, NoTimeout_NoRandomSave) { - // When there's no timeout, select may never return so set a timer. - SetTimer(absl::Milliseconds(100)); - // See that we get interrupted by the timer. - ASSERT_THAT(select(1, nullptr, nullptr, nullptr, nullptr), - SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); -} - -TEST_F(SelectTest, InvalidTimeoutNegative) { - struct timeval timeout = absl::ToTimeval(absl::Microseconds(-1)); - EXPECT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), - SyscallFailsWithErrno(EINVAL)); - // Ignore timeout as its value is now undefined. -} - -// Verify that a signal interrupts select. -// -// If random S/R interrupts the select, SIGALRM may be delivered before select -// restarts, causing the select to hang forever. -TEST_F(SelectTest, InterruptedBySignal_NoRandomSave) { - absl::Duration duration(absl::Seconds(5)); - struct timeval timeout = absl::ToTimeval(duration); - SetTimer(absl::Milliseconds(100)); - ASSERT_FALSE(TimerFired()); - ASSERT_THAT(select(1, nullptr, nullptr, nullptr, &timeout), - SyscallFailsWithErrno(EINTR)); - EXPECT_TRUE(TimerFired()); - // Ignore timeout as its value is now undefined. -} - -TEST_F(SelectTest, IgnoreBitsAboveNfds) { - // fd_set is a bit array with at least FD_SETSIZE bits. Test that bits - // corresponding to file descriptors above nfds are ignored. - fd_set read_set; - FD_ZERO(&read_set); - constexpr int kNfds = 1; - for (int fd = kNfds; fd < FD_SETSIZE; fd++) { - FD_SET(fd, &read_set); - } - // Pass a zero timeout so that select returns immediately. - struct timeval timeout = {}; - EXPECT_THAT(select(kNfds, &read_set, nullptr, nullptr, &timeout), - SyscallSucceedsWithValue(0)); -} - -// This test illustrates Linux's behavior of 'select' calls passing after -// setrlimit RLIMIT_NOFILE is called. In particular, versions of sshd rely on -// this behavior. See b/122318458. -TEST_F(SelectTest, SetrlimitCallNOFILE) { - fd_set read_set; - FD_ZERO(&read_set); - timeval timeout = {}; - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(NewTempAbsPath(), O_RDONLY | O_CREAT, S_IRUSR)); - - Cleanup reset_rlimit = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_NOFILE, 0)); - - FD_SET(fd.get(), &read_set); - // this call with zero timeout should return immediately - EXPECT_THAT(select(fd.get() + 1, &read_set, nullptr, nullptr, &timeout), - SyscallSucceeds()); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc deleted file mode 100644 index 28f51a3bf..000000000 --- a/test/syscalls/linux/semaphore.cc +++ /dev/null @@ -1,1015 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/ipc.h> -#include <sys/sem.h> -#include <sys/types.h> - -#include <atomic> -#include <cerrno> -#include <ctime> -#include <set> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "absl/memory/memory.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/clock.h" -#include "test/util/capability_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { -namespace { - -constexpr int kSemMap = 1024000000; -constexpr int kSemMni = 32000; -constexpr int kSemMns = 1024000000; -constexpr int kSemMnu = 1024000000; -constexpr int kSemMsl = 32000; -constexpr int kSemOpm = 500; -constexpr int kSemUme = 500; -constexpr int kSemUsz = 20; -constexpr int kSemVmx = 32767; -constexpr int kSemAem = 32767; - -class AutoSem { - public: - explicit AutoSem(int id) : id_(id) {} - ~AutoSem() { - if (id_ >= 0) { - EXPECT_THAT(semctl(id_, 0, IPC_RMID), SyscallSucceeds()); - } - } - - int release() { - int old = id_; - id_ = -1; - return old; - } - - int get() { return id_; } - - private: - int id_ = -1; -}; - -bool operator==(struct semid_ds const& a, struct semid_ds const& b) { - return a.sem_perm.__key == b.sem_perm.__key && - a.sem_perm.uid == b.sem_perm.uid && a.sem_perm.gid == b.sem_perm.gid && - a.sem_perm.cuid == b.sem_perm.cuid && - a.sem_perm.cgid == b.sem_perm.cgid && - a.sem_perm.mode == b.sem_perm.mode && a.sem_otime == b.sem_otime && - a.sem_ctime == b.sem_ctime && a.sem_nsems == b.sem_nsems; -} - -TEST(SemaphoreTest, SemGet) { - // Test creation and lookup. - AutoSem sem(semget(1, 10, IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - EXPECT_THAT(semget(1, 10, IPC_CREAT), SyscallSucceedsWithValue(sem.get())); - EXPECT_THAT(semget(1, 9, IPC_CREAT), SyscallSucceedsWithValue(sem.get())); - - // Creation and lookup failure cases. - EXPECT_THAT(semget(1, 11, IPC_CREAT), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(semget(1, -1, IPC_CREAT), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(semget(1, 10, IPC_CREAT | IPC_EXCL), - SyscallFailsWithErrno(EEXIST)); - EXPECT_THAT(semget(2, 1, 0), SyscallFailsWithErrno(ENOENT)); - EXPECT_THAT(semget(2, 0, IPC_CREAT), SyscallFailsWithErrno(EINVAL)); - - // Private semaphores never conflict. - AutoSem sem2(semget(IPC_PRIVATE, 1, 0)); - AutoSem sem3(semget(IPC_PRIVATE, 1, 0)); - ASSERT_THAT(sem2.get(), SyscallSucceeds()); - EXPECT_NE(sem.get(), sem2.get()); - ASSERT_THAT(sem3.get(), SyscallSucceeds()); - EXPECT_NE(sem3.get(), sem2.get()); -} - -// Tests simple operations that shouldn't block in a single-thread. -TEST(SemaphoreTest, SemOpSingleNoBlock) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - struct sembuf buf = {}; - buf.sem_op = 1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - - buf.sem_op = -1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - - buf.sem_op = 0; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - - // Error cases with invalid values. - ASSERT_THAT(semop(sem.get() + 1, &buf, 1), SyscallFailsWithErrno(EINVAL)); - - buf.sem_num = 1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EFBIG)); - - ASSERT_THAT(semop(sem.get(), nullptr, 0), SyscallFailsWithErrno(EINVAL)); -} - -// Tests simple timed operations that shouldn't block in a single-thread. -TEST(SemaphoreTest, SemTimedOpSingleNoBlock) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - struct sembuf buf = {}; - buf.sem_op = 1; - struct timespec timeout = {}; - // 50 milliseconds. - timeout.tv_nsec = 5e7; - ASSERT_THAT(semtimedop(sem.get(), &buf, 1, &timeout), SyscallSucceeds()); - - buf.sem_op = -1; - EXPECT_THAT(semtimedop(sem.get(), &buf, 1, &timeout), SyscallSucceeds()); - - buf.sem_op = 0; - EXPECT_THAT(semtimedop(sem.get(), &buf, 1, &timeout), SyscallSucceeds()); - - // Error cases with invalid values. - EXPECT_THAT(semtimedop(sem.get() + 1, &buf, 1, &timeout), - SyscallFailsWithErrno(EINVAL)); - - buf.sem_num = 1; - EXPECT_THAT(semtimedop(sem.get(), &buf, 1, &timeout), - SyscallFailsWithErrno(EFBIG)); - buf.sem_num = 0; - - EXPECT_THAT(semtimedop(sem.get(), nullptr, 0, &timeout), - SyscallFailsWithErrno(EINVAL)); - - timeout.tv_nsec = 1e9; - EXPECT_THAT(semtimedop(sem.get(), &buf, 0, &timeout), - SyscallFailsWithErrno(EINVAL)); -} - -// Tests multiple operations that shouldn't block in a single-thread. -TEST(SemaphoreTest, SemOpMultiNoBlock) { - AutoSem sem(semget(IPC_PRIVATE, 4, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - struct sembuf bufs[5] = {}; - bufs[0].sem_num = 0; - bufs[0].sem_op = 10; - bufs[0].sem_flg = 0; - - bufs[1].sem_num = 1; - bufs[1].sem_op = 2; - bufs[1].sem_flg = 0; - - bufs[2].sem_num = 2; - bufs[2].sem_op = 3; - bufs[2].sem_flg = 0; - - bufs[3].sem_num = 0; - bufs[3].sem_op = -5; - bufs[3].sem_flg = 0; - - bufs[4].sem_num = 2; - bufs[4].sem_op = 2; - bufs[4].sem_flg = 0; - - ASSERT_THAT(semop(sem.get(), bufs, ABSL_ARRAYSIZE(bufs)), SyscallSucceeds()); - - ASSERT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(5)); - ASSERT_THAT(semctl(sem.get(), 1, GETVAL), SyscallSucceedsWithValue(2)); - ASSERT_THAT(semctl(sem.get(), 2, GETVAL), SyscallSucceedsWithValue(5)); - ASSERT_THAT(semctl(sem.get(), 3, GETVAL), SyscallSucceedsWithValue(0)); - - for (auto& b : bufs) { - b.sem_op = -b.sem_op; - } - // 0 and 3 order must be reversed, otherwise it will block. - std::swap(bufs[0].sem_op, bufs[3].sem_op); - ASSERT_THAT(RetryEINTR(semop)(sem.get(), bufs, ABSL_ARRAYSIZE(bufs)), - SyscallSucceeds()); - - // All semaphores should be back to 0 now. - for (size_t i = 0; i < 4; ++i) { - ASSERT_THAT(semctl(sem.get(), i, GETVAL), SyscallSucceedsWithValue(0)); - } -} - -// Makes a best effort attempt to ensure that operation would block. -TEST(SemaphoreTest, SemOpBlock) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - std::atomic<int> blocked = ATOMIC_VAR_INIT(1); - ScopedThread th([&sem, &blocked] { - absl::SleepFor(absl::Milliseconds(100)); - ASSERT_EQ(blocked.load(), 1); - - struct sembuf buf = {}; - buf.sem_op = 1; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - }); - - struct sembuf buf = {}; - buf.sem_op = -1; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - blocked.store(0); -} - -// Makes a best effort attempt to ensure that operation would be timeout when -// being blocked. -TEST(SemaphoreTest, SemTimedOpBlock) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - ScopedThread th([&sem] { - absl::SleepFor(absl::Milliseconds(100)); - - struct sembuf buf = {}; - buf.sem_op = 1; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - }); - - struct sembuf buf = {}; - buf.sem_op = -1; - struct timespec timeout = {}; - timeout.tv_nsec = 5e7; - // semtimedop reaches the time limit, it fails with errno EAGAIN. - ASSERT_THAT(RetryEINTR(semtimedop)(sem.get(), &buf, 1, &timeout), - SyscallFailsWithErrno(EAGAIN)); -} - -// Tests that IPC_NOWAIT returns with no wait. -TEST(SemaphoreTest, SemOpNoBlock) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - struct sembuf buf = {}; - buf.sem_flg = IPC_NOWAIT; - - buf.sem_op = -1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EAGAIN)); - - buf.sem_op = 1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - - buf.sem_op = 0; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EAGAIN)); -} - -// Test runs 2 threads, one signals the other waits the same number of times. -TEST(SemaphoreTest, SemOpSimple) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - constexpr size_t kLoops = 100; - ScopedThread th([&sem] { - struct sembuf buf = {}; - buf.sem_op = 1; - for (size_t i = 0; i < kLoops; i++) { - // Sleep to prevent making all increments in one shot without letting - // the waiter wait. - absl::SleepFor(absl::Milliseconds(1)); - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - } - }); - - struct sembuf buf = {}; - buf.sem_op = -1; - for (size_t i = 0; i < kLoops; i++) { - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - } -} - -// Tests that semaphore can be removed while there are waiters. -// NoRandomSave: Test relies on timing that random save throws off. -TEST(SemaphoreTest, SemOpRemoveWithWaiter_NoRandomSave) { - AutoSem sem(semget(IPC_PRIVATE, 2, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - ScopedThread th([&sem] { - absl::SleepFor(absl::Milliseconds(250)); - ASSERT_THAT(semctl(sem.release(), 0, IPC_RMID), SyscallSucceeds()); - }); - - // This must happen before IPC_RMID runs above. Otherwise it fails with EINVAL - // instead because the semaphore has already been removed. - struct sembuf buf = {}; - buf.sem_op = -1; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), - SyscallFailsWithErrno(EIDRM)); -} - -// Semaphore isn't fair. It will execute any waiter that can satisfy the -// request even if it gets in front of other waiters. -TEST(SemaphoreTest, SemOpBestFitExecution) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - ScopedThread th([&sem] { - struct sembuf buf = {}; - buf.sem_op = -2; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallFails()); - // Ensure that wait will only unblock when the semaphore is removed. On - // EINTR retry it may race with deletion and return EINVAL. - ASSERT_TRUE(errno == EIDRM || errno == EINVAL) << "errno=" << errno; - }); - - // Ensures that '-1' below will unblock even though '-10' above is waiting - // for the same semaphore. - for (size_t i = 0; i < 10; ++i) { - struct sembuf buf = {}; - buf.sem_op = 1; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - - absl::SleepFor(absl::Milliseconds(10)); - - buf.sem_op = -1; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - } - - ASSERT_THAT(semctl(sem.release(), 0, IPC_RMID), SyscallSucceeds()); -} - -// Executes random operations in multiple threads and verify correctness. -TEST(SemaphoreTest, SemOpRandom) { - // Don't do cooperative S/R tests because there are too many syscalls in - // this test, - const DisableSave ds; - - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - // Protects the seed below. - absl::Mutex mutex; - uint32_t seed = time(nullptr); - - int count = 0; // Tracks semaphore value. - bool done = false; // Tells waiters to stop after signal threads are done. - - // These threads will wait in a loop. - std::unique_ptr<ScopedThread> decs[5]; - for (auto& dec : decs) { - dec = absl::make_unique<ScopedThread>([&sem, &mutex, &count, &seed, &done] { - for (size_t i = 0; i < 500; ++i) { - int16_t val; - { - absl::MutexLock l(&mutex); - if (done) { - return; - } - val = (rand_r(&seed) % 10 + 1); // Rand between 1 and 10. - count -= val; - } - struct sembuf buf = {}; - buf.sem_op = -val; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - absl::SleepFor(absl::Milliseconds(val * 2)); - } - }); - } - - // These threads will wait for zero in a loop. - std::unique_ptr<ScopedThread> zeros[5]; - for (auto& zero : zeros) { - zero = absl::make_unique<ScopedThread>([&sem, &mutex, &done] { - for (size_t i = 0; i < 500; ++i) { - { - absl::MutexLock l(&mutex); - if (done) { - return; - } - } - struct sembuf buf = {}; - buf.sem_op = 0; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - absl::SleepFor(absl::Milliseconds(10)); - } - }); - } - - // These threads will signal in a loop. - std::unique_ptr<ScopedThread> incs[5]; - for (auto& inc : incs) { - inc = absl::make_unique<ScopedThread>([&sem, &mutex, &count, &seed] { - for (size_t i = 0; i < 500; ++i) { - int16_t val; - { - absl::MutexLock l(&mutex); - val = (rand_r(&seed) % 10 + 1); // Rand between 1 and 10. - count += val; - } - struct sembuf buf = {}; - buf.sem_op = val; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - absl::SleepFor(absl::Milliseconds(val * 2)); - } - }); - } - - // First wait for signal threads to be done. - for (auto& inc : incs) { - inc->Join(); - } - - // Now there could be waiters blocked (remember operations are random). - // Notify waiters that we're done and signal semaphore just the right amount. - { - absl::MutexLock l(&mutex); - done = true; - struct sembuf buf = {}; - buf.sem_op = -count; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - } - - // Now all waiters should unblock and exit. - for (auto& dec : decs) { - dec->Join(); - } - for (auto& zero : zeros) { - zero->Join(); - } -} - -TEST(SemaphoreTest, SemOpNamespace) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - AutoSem sem(semget(123, 1, 0600 | IPC_CREAT | IPC_EXCL)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - ScopedThread([]() { - EXPECT_THAT(unshare(CLONE_NEWIPC), SyscallSucceeds()); - AutoSem sem(semget(123, 1, 0600 | IPC_CREAT | IPC_EXCL)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - }); -} - -TEST(SemaphoreTest, SemCtlVal) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - // Semaphore must start with 0. - EXPECT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(0)); - - // Increase value and ensure waiters are woken up. - ScopedThread th([&sem] { - struct sembuf buf = {}; - buf.sem_op = -10; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - }); - - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 9), SyscallSucceeds()); - EXPECT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(9)); - - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 20), SyscallSucceeds()); - const int value = semctl(sem.get(), 0, GETVAL); - // 10 or 20 because it could have raced with waiter above. - EXPECT_TRUE(value == 10 || value == 20) << "value=" << value; - th.Join(); - - // Set it back to 0 and ensure that waiters are woken up. - ScopedThread thZero([&sem] { - struct sembuf buf = {}; - buf.sem_op = 0; - ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1), SyscallSucceeds()); - }); - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 0), SyscallSucceeds()); - EXPECT_THAT(semctl(sem.get(), 0, GETVAL), SyscallSucceedsWithValue(0)); - thZero.Join(); -} - -TEST(SemaphoreTest, SemCtlValAll) { - AutoSem sem(semget(IPC_PRIVATE, 3, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - // Semaphores must start with 0. - uint16_t get[3] = {10, 10, 10}; - EXPECT_THAT(semctl(sem.get(), 1, GETALL, get), SyscallSucceedsWithValue(0)); - for (auto v : get) { - EXPECT_EQ(v, 0); - } - - // SetAll and check that they were set. - uint16_t vals[3] = {0, 10, 20}; - EXPECT_THAT(semctl(sem.get(), 1, SETALL, vals), SyscallSucceedsWithValue(0)); - EXPECT_THAT(semctl(sem.get(), 1, GETALL, get), SyscallSucceedsWithValue(0)); - for (size_t i = 0; i < ABSL_ARRAYSIZE(vals); ++i) { - EXPECT_EQ(get[i], vals[i]); - } - - EXPECT_THAT(semctl(sem.get(), 1, SETALL, nullptr), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(SemaphoreTest, SemCtlGetPid) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds()); - EXPECT_THAT(semctl(sem.get(), 0, GETPID), SyscallSucceedsWithValue(getpid())); -} - -TEST(SemaphoreTest, SemCtlGetPidFork) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - const pid_t child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(semctl(sem.get(), 0, SETVAL, 1) == 0); - TEST_PCHECK(semctl(sem.get(), 0, GETPID) == getpid()); - - _exit(0); - } - ASSERT_THAT(child_pid, SyscallSucceeds()); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << " status " << status; -} - -TEST(SemaphoreTest, SemIpcSet) { - // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); - - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - struct semid_ds semid = {}; - semid.sem_perm.uid = getuid(); - semid.sem_perm.gid = getgid(); - - // Make semaphore readonly and check that signal fails. - semid.sem_perm.mode = 0400; - EXPECT_THAT(semctl(sem.get(), 0, IPC_SET, &semid), SyscallSucceeds()); - struct sembuf buf = {}; - buf.sem_op = 1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EACCES)); - - // Make semaphore writeonly and check that wait for zero fails. - semid.sem_perm.mode = 0200; - EXPECT_THAT(semctl(sem.get(), 0, IPC_SET, &semid), SyscallSucceeds()); - buf.sem_op = 0; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EACCES)); -} - -TEST(SemaphoreTest, SemCtlIpcStat) { - // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); - const uid_t kUid = getuid(); - const gid_t kGid = getgid(); - time_t start_time = time(nullptr); - - AutoSem sem(semget(IPC_PRIVATE, 10, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - struct semid_ds ds; - EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), SyscallSucceeds()); - - EXPECT_EQ(ds.sem_perm.__key, IPC_PRIVATE); - EXPECT_EQ(ds.sem_perm.uid, kUid); - EXPECT_EQ(ds.sem_perm.gid, kGid); - EXPECT_EQ(ds.sem_perm.cuid, kUid); - EXPECT_EQ(ds.sem_perm.cgid, kGid); - EXPECT_EQ(ds.sem_perm.mode, 0600); - // Last semop time is not set on creation. - EXPECT_EQ(ds.sem_otime, 0); - EXPECT_GE(ds.sem_ctime, start_time); - EXPECT_EQ(ds.sem_nsems, 10); - - // The timestamps only have a resolution of seconds; slow down so we actually - // see the timestamps change. - absl::SleepFor(absl::Seconds(1)); - - // Set semid_ds structure of the set. - auto last_ctime = ds.sem_ctime; - start_time = time(nullptr); - struct semid_ds semid_to_set = {}; - semid_to_set.sem_perm.uid = kUid; - semid_to_set.sem_perm.gid = kGid; - semid_to_set.sem_perm.mode = 0666; - ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &semid_to_set), SyscallSucceeds()); - struct sembuf buf = {}; - buf.sem_op = 1; - ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); - - EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), SyscallSucceeds()); - EXPECT_EQ(ds.sem_perm.mode, 0666); - EXPECT_GE(ds.sem_otime, start_time); - EXPECT_GT(ds.sem_ctime, last_ctime); - - // An invalid semid fails the syscall with errno EINVAL. - EXPECT_THAT(semctl(sem.get() + 1, 0, IPC_STAT, &ds), - SyscallFailsWithErrno(EINVAL)); - - // Make semaphore not readable and check the signal fails. - semid_to_set.sem_perm.mode = 0200; - ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &semid_to_set), SyscallSucceeds()); - EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), - SyscallFailsWithErrno(EACCES)); -} - -// Calls semctl(semid, 0, cmd) until the returned value is >= target, an -// internal timeout expires, or semctl returns an error. -PosixErrorOr<int> WaitSemctl(int semid, int target, int cmd) { - constexpr absl::Duration timeout = absl::Seconds(10); - const auto deadline = absl::Now() + timeout; - int semcnt = 0; - while (absl::Now() < deadline) { - semcnt = semctl(semid, 0, cmd); - if (semcnt < 0) { - return PosixError(errno, "semctl(GETZCNT) failed"); - } - if (semcnt >= target) { - break; - } - absl::SleepFor(absl::Milliseconds(10)); - } - return semcnt; -} - -TEST(SemaphoreTest, SemopGetzcnt) { - // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); - // Create a write only semaphore set. - AutoSem sem(semget(IPC_PRIVATE, 1, 0200 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - // No read permission to retrieve semzcnt. - EXPECT_THAT(semctl(sem.get(), 0, GETZCNT), SyscallFailsWithErrno(EACCES)); - - // Remove the calling thread's read permission. - struct semid_ds ds = {}; - ds.sem_perm.uid = getuid(); - ds.sem_perm.gid = getgid(); - ds.sem_perm.mode = 0600; - ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &ds), SyscallSucceeds()); - - std::vector<pid_t> children; - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds()); - - struct sembuf buf = {}; - buf.sem_num = 0; - buf.sem_op = 0; - constexpr size_t kLoops = 10; - for (size_t i = 0; i < kLoops; i++) { - auto child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(RetryEINTR(semop)(sem.get(), &buf, 1) == 0); - _exit(0); - } - children.push_back(child_pid); - } - - EXPECT_THAT(WaitSemctl(sem.get(), kLoops, GETZCNT), - IsPosixErrorOkAndHolds(kLoops)); - // Set semval to 0, which wakes up children that sleep on the semop. - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 0), SyscallSucceeds()); - for (const auto& child_pid : children) { - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - } - EXPECT_EQ(semctl(sem.get(), 0, GETZCNT), 0); -} - -TEST(SemaphoreTest, SemopGetzcntOnSetRemoval) { - auto semid = semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT); - ASSERT_THAT(semid, SyscallSucceeds()); - ASSERT_THAT(semctl(semid, 0, SETVAL, 1), SyscallSucceeds()); - ASSERT_EQ(semctl(semid, 0, GETZCNT), 0); - - auto child_pid = fork(); - if (child_pid == 0) { - struct sembuf buf = {}; - buf.sem_num = 0; - buf.sem_op = 0; - - // Ensure that wait will only unblock when the semaphore is removed. On - // EINTR retry it may race with deletion and return EINVAL. - TEST_PCHECK(RetryEINTR(semop)(semid, &buf, 1) < 0 && - (errno == EIDRM || errno == EINVAL)); - _exit(0); - } - - EXPECT_THAT(WaitSemctl(semid, 1, GETZCNT), IsPosixErrorOkAndHolds(1)); - // Remove the semaphore set, which fails the sleep semop. - ASSERT_THAT(semctl(semid, 0, IPC_RMID), SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - EXPECT_THAT(semctl(semid, 0, GETZCNT), SyscallFailsWithErrno(EINVAL)); -} - -TEST(SemaphoreTest, SemopGetzcntOnSignal_NoRandomSave) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds()); - ASSERT_EQ(semctl(sem.get(), 0, GETZCNT), 0); - - // Saving will cause semop() to be spuriously interrupted. - DisableSave ds; - - auto child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(signal(SIGHUP, [](int sig) -> void {}) != SIG_ERR); - struct sembuf buf = {}; - buf.sem_num = 0; - buf.sem_op = 0; - - TEST_PCHECK(semop(sem.get(), &buf, 1) < 0 && errno == EINTR); - _exit(0); - } - - EXPECT_THAT(WaitSemctl(sem.get(), 1, GETZCNT), IsPosixErrorOkAndHolds(1)); - // Send a signal to the child, which fails the sleep semop. - ASSERT_EQ(kill(child_pid, SIGHUP), 0); - - ds.reset(); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - EXPECT_EQ(semctl(sem.get(), 0, GETZCNT), 0); -} - -TEST(SemaphoreTest, SemopGetncnt) { - // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); - // Create a write only semaphore set. - AutoSem sem(semget(IPC_PRIVATE, 1, 0200 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - - // No read permission to retrieve semzcnt. - EXPECT_THAT(semctl(sem.get(), 0, GETNCNT), SyscallFailsWithErrno(EACCES)); - - // Remove the calling thread's read permission. - struct semid_ds ds = {}; - ds.sem_perm.uid = getuid(); - ds.sem_perm.gid = getgid(); - ds.sem_perm.mode = 0600; - ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &ds), SyscallSucceeds()); - - std::vector<pid_t> children; - - struct sembuf buf = {}; - buf.sem_num = 0; - buf.sem_op = -1; - constexpr size_t kLoops = 10; - for (size_t i = 0; i < kLoops; i++) { - auto child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(RetryEINTR(semop)(sem.get(), &buf, 1) == 0); - _exit(0); - } - children.push_back(child_pid); - } - EXPECT_THAT(WaitSemctl(sem.get(), kLoops, GETNCNT), - IsPosixErrorOkAndHolds(kLoops)); - // Set semval to 1, which wakes up children that sleep on the semop. - ASSERT_THAT(semctl(sem.get(), 0, SETVAL, kLoops), SyscallSucceeds()); - for (const auto& child_pid : children) { - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - } - EXPECT_EQ(semctl(sem.get(), 0, GETNCNT), 0); -} - -TEST(SemaphoreTest, SemopGetncntOnSetRemoval) { - auto semid = semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT); - ASSERT_THAT(semid, SyscallSucceeds()); - ASSERT_EQ(semctl(semid, 0, GETNCNT), 0); - - auto child_pid = fork(); - if (child_pid == 0) { - struct sembuf buf = {}; - buf.sem_num = 0; - buf.sem_op = -1; - - // Ensure that wait will only unblock when the semaphore is removed. On - // EINTR retry it may race with deletion and return EINVAL - TEST_PCHECK(RetryEINTR(semop)(semid, &buf, 1) < 0 && - (errno == EIDRM || errno == EINVAL)); - _exit(0); - } - - EXPECT_THAT(WaitSemctl(semid, 1, GETNCNT), IsPosixErrorOkAndHolds(1)); - // Remove the semaphore set, which fails the sleep semop. - ASSERT_THAT(semctl(semid, 0, IPC_RMID), SyscallSucceeds()); - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - EXPECT_THAT(semctl(semid, 0, GETNCNT), SyscallFailsWithErrno(EINVAL)); -} - -TEST(SemaphoreTest, SemopGetncntOnSignal_NoRandomSave) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - ASSERT_EQ(semctl(sem.get(), 0, GETNCNT), 0); - - // Saving will cause semop() to be spuriously interrupted. - DisableSave ds; - - auto child_pid = fork(); - if (child_pid == 0) { - TEST_PCHECK(signal(SIGHUP, [](int sig) -> void {}) != SIG_ERR); - struct sembuf buf = {}; - buf.sem_num = 0; - buf.sem_op = -1; - - TEST_PCHECK(semop(sem.get(), &buf, 1) < 0 && errno == EINTR); - _exit(0); - } - EXPECT_THAT(WaitSemctl(sem.get(), 1, GETNCNT), IsPosixErrorOkAndHolds(1)); - // Send a signal to the child, which fails the sleep semop. - ASSERT_EQ(kill(child_pid, SIGHUP), 0); - - ds.reset(); - - int status; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); - EXPECT_EQ(semctl(sem.get(), 0, GETNCNT), 0); -} - -#ifndef SEM_STAT_ANY -#define SEM_STAT_ANY 20 -#endif // SEM_STAT_ANY - -TEST(SemaphoreTest, IpcInfo) { - constexpr int kLoops = 5; - std::set<int> sem_ids; - struct seminfo info; - // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); - for (int i = 0; i < kLoops; i++) { - AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - sem_ids.insert(sem.release()); - } - ASSERT_EQ(sem_ids.size(), kLoops); - - int max_used_index = 0; - EXPECT_THAT(max_used_index = semctl(0, 0, IPC_INFO, &info), - SyscallSucceeds()); - - std::set<int> sem_ids_before_max_index; - for (int i = 0; i <= max_used_index; i++) { - struct semid_ds ds = {}; - int sem_id = semctl(i, 0, SEM_STAT, &ds); - // Only if index i is used within the registry. - if (sem_ids.find(sem_id) != sem_ids.end()) { - struct semid_ds ipc_stat_ds; - ASSERT_THAT(semctl(sem_id, 0, IPC_STAT, &ipc_stat_ds), SyscallSucceeds()); - EXPECT_TRUE(ds == ipc_stat_ds); - - // Remove the semaphore set's read permission. - struct semid_ds ipc_set_ds; - ipc_set_ds.sem_perm.uid = getuid(); - ipc_set_ds.sem_perm.gid = getgid(); - // Keep the semaphore set's write permission so that it could be removed. - ipc_set_ds.sem_perm.mode = 0200; - // IPC_SET command here updates sem_ctime member of the sem. - ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds()); - ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES)); - int val = semctl(i, 0, SEM_STAT_ANY, &ds); - if (val == -1) { - // Only if the kernel doesn't support the command SEM_STAT_ANY. - EXPECT_TRUE(errno == EINVAL || errno == EFAULT); - } else { - EXPECT_EQ(sem_id, val); - EXPECT_LE(ipc_stat_ds.sem_ctime, ds.sem_ctime); - ipc_stat_ds.sem_ctime = 0; - ipc_stat_ds.sem_perm.mode = 0200; - ds.sem_ctime = 0; - EXPECT_TRUE(ipc_stat_ds == ds); - } - sem_ids_before_max_index.insert(sem_id); - } - } - EXPECT_EQ(sem_ids_before_max_index.size(), kLoops); - for (const int sem_id : sem_ids) { - ASSERT_THAT(semctl(sem_id, 0, IPC_RMID), SyscallSucceeds()); - } - - ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceeds()); - EXPECT_EQ(info.semmap, kSemMap); - EXPECT_EQ(info.semmni, kSemMni); - EXPECT_EQ(info.semmns, kSemMns); - EXPECT_EQ(info.semmnu, kSemMnu); - EXPECT_EQ(info.semmsl, kSemMsl); - EXPECT_EQ(info.semopm, kSemOpm); - EXPECT_EQ(info.semume, kSemUme); - EXPECT_EQ(info.semusz, kSemUsz); - EXPECT_EQ(info.semvmx, kSemVmx); - EXPECT_EQ(info.semaem, kSemAem); -} - -TEST(SemaphoreTest, SemInfo) { - constexpr int kLoops = 5; - constexpr int kSemSetSize = 3; - std::set<int> sem_ids; - struct seminfo info; - // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); - for (int i = 0; i < kLoops; i++) { - AutoSem sem(semget(IPC_PRIVATE, kSemSetSize, 0600 | IPC_CREAT)); - ASSERT_THAT(sem.get(), SyscallSucceeds()); - sem_ids.insert(sem.release()); - } - ASSERT_EQ(sem_ids.size(), kLoops); - int max_used_index = 0; - EXPECT_THAT(max_used_index = semctl(0, 0, SEM_INFO, &info), - SyscallSucceeds()); - EXPECT_EQ(info.semmap, kSemMap); - EXPECT_EQ(info.semmni, kSemMni); - EXPECT_EQ(info.semmns, kSemMns); - EXPECT_EQ(info.semmnu, kSemMnu); - EXPECT_EQ(info.semmsl, kSemMsl); - EXPECT_EQ(info.semopm, kSemOpm); - EXPECT_EQ(info.semume, kSemUme); - // There could be semaphores existing in the system during the test, which - // prevents the test from getting a exact number, but the test could expect at - // least the number of sempahroes it creates in the begining of the test. - EXPECT_GE(info.semusz, sem_ids.size()); - EXPECT_EQ(info.semvmx, kSemVmx); - EXPECT_GE(info.semaem, sem_ids.size() * kSemSetSize); - - std::set<int> sem_ids_before_max_index; - for (int i = 0; i <= max_used_index; i++) { - struct semid_ds ds = {}; - int sem_id = semctl(i, 0, SEM_STAT, &ds); - // Only if index i is used within the registry. - if (sem_ids.find(sem_id) != sem_ids.end()) { - struct semid_ds ipc_stat_ds; - ASSERT_THAT(semctl(sem_id, 0, IPC_STAT, &ipc_stat_ds), SyscallSucceeds()); - EXPECT_TRUE(ds == ipc_stat_ds); - - // Remove the semaphore set's read permission. - struct semid_ds ipc_set_ds; - ipc_set_ds.sem_perm.uid = getuid(); - ipc_set_ds.sem_perm.gid = getgid(); - // Keep the semaphore set's write permission so that it could be removed. - ipc_set_ds.sem_perm.mode = 0200; - // IPC_SET command here updates sem_ctime member of the sem. - ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds()); - ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES)); - int val = semctl(i, 0, SEM_STAT_ANY, &ds); - - if (val == -1) { - // Only if the kernel doesn't support the command SEM_STAT_ANY. - EXPECT_TRUE(errno == EINVAL || errno == EFAULT); - } else { - EXPECT_EQ(val, sem_id); - EXPECT_LE(ipc_stat_ds.sem_ctime, ds.sem_ctime); - ipc_stat_ds.sem_ctime = 0; - ipc_stat_ds.sem_perm.mode = 0200; - ds.sem_ctime = 0; - EXPECT_TRUE(ipc_stat_ds == ds); - } - sem_ids_before_max_index.insert(sem_id); - } - } - EXPECT_EQ(sem_ids_before_max_index.size(), kLoops); - for (const int sem_id : sem_ids) { - ASSERT_THAT(semctl(sem_id, 0, IPC_RMID), SyscallSucceeds()); - } - - ASSERT_THAT(semctl(0, 0, SEM_INFO, &info), SyscallSucceeds()); - EXPECT_EQ(info.semmap, kSemMap); - EXPECT_EQ(info.semmni, kSemMni); - EXPECT_EQ(info.semmns, kSemMns); - EXPECT_EQ(info.semmnu, kSemMnu); - EXPECT_EQ(info.semmsl, kSemMsl); - EXPECT_EQ(info.semopm, kSemOpm); - EXPECT_EQ(info.semume, kSemUme); - // Apart from semapahores that are not created by the test, we can't determine - // the exact number of semaphore sets and semaphores, as a result, semusz and - // semaem range from 0 to a random number. Since the numbers are always - // non-negative, the test will not check the reslts of semusz and semaem. - EXPECT_EQ(info.semvmx, kSemVmx); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc deleted file mode 100644 index 93b3a94f1..000000000 --- a/test/syscalls/linux/sendfile.cc +++ /dev/null @@ -1,729 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <linux/unistd.h> -#include <sys/eventfd.h> -#include <sys/sendfile.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/eventfd_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/signal_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SendFileTest, SendZeroBytes) { - // Create temp files. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct value. - EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, 0), - SyscallSucceedsWithValue(0)); -} - -TEST(SendFileTest, InvalidOffset) { - // Create temp files. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct value. - off_t offset = -1; - EXPECT_THAT(sendfile(outf.get(), inf.get(), &offset, 0), - SyscallFailsWithErrno(EINVAL)); -} - -int memfd_create(const std::string& name, unsigned int flags) { - return syscall(__NR_memfd_create, name.c_str(), flags); -} - -TEST(SendFileTest, Overflow) { - // Create input file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file. - int fd; - EXPECT_THAT(fd = memfd_create("overflow", 0), SyscallSucceeds()); - const FileDescriptor outf(fd); - - // out_offset + kSize overflows INT64_MAX. - loff_t out_offset = 0x7ffffffffffffffeull; - constexpr int kSize = 3; - EXPECT_THAT(sendfile(outf.get(), inf.get(), &out_offset, kSize), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SendFileTest, SendTrivially) { - // Create temp files. - constexpr char kData[] = "To be, or not to be, that is the question:"; - constexpr int kDataSize = sizeof(kData) - 1; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - FileDescriptor outf; - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT(bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize), - SyscallSucceedsWithValue(kDataSize)); - - // Close outf to avoid leak. - outf.reset(); - - // Open the output file as read only. - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Verify that the output file has the correct data. - char actual[kDataSize]; - ASSERT_THAT(read(outf.get(), &actual, bytes_sent), - SyscallSucceedsWithValue(kDataSize)); - EXPECT_EQ(kData, absl::string_view(actual, bytes_sent)); -} - -TEST(SendFileTest, SendTriviallyWithBothFilesReadWrite) { - // Create temp files. - constexpr char kData[] = "Whether 'tis nobler in the mind to suffer"; - constexpr int kDataSize = sizeof(kData) - 1; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as readwrite. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - - // Open the output file as readwrite. - FileDescriptor outf; - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR)); - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT(bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize), - SyscallSucceedsWithValue(kDataSize)); - - // Close outf to avoid leak. - outf.reset(); - - // Open the output file as read only. - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Verify that the output file has the correct data. - char actual[kDataSize]; - ASSERT_THAT(read(outf.get(), &actual, bytes_sent), - SyscallSucceedsWithValue(kDataSize)); - EXPECT_EQ(kData, absl::string_view(actual, bytes_sent)); -} - -TEST(SendFileTest, SendAndUpdateFileOffset) { - // Create temp files. - // Test input string length must be > 2 AND even. - constexpr char kData[] = "The slings and arrows of outrageous fortune,"; - constexpr int kDataSize = sizeof(kData) - 1; - constexpr int kHalfDataSize = kDataSize / 2; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - FileDescriptor outf; - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT( - bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize), - SyscallSucceedsWithValue(kHalfDataSize)); - - // Close outf to avoid leak. - outf.reset(); - - // Open the output file as read only. - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Verify that the output file has the correct data. - char actual[kHalfDataSize]; - ASSERT_THAT(read(outf.get(), &actual, bytes_sent), - SyscallSucceedsWithValue(kHalfDataSize)); - EXPECT_EQ(absl::string_view(kData, kHalfDataSize), - absl::string_view(actual, bytes_sent)); - - // Verify that the input file offset has been updated. - ASSERT_THAT(read(inf.get(), &actual, kDataSize - bytes_sent), - SyscallSucceedsWithValue(kHalfDataSize)); - EXPECT_EQ( - absl::string_view(kData + kDataSize - bytes_sent, kDataSize - bytes_sent), - absl::string_view(actual, kHalfDataSize)); -} - -TEST(SendFileTest, SendToDevZeroAndUpdateFileOffset) { - // Create temp files. - // Test input string length must be > 2 AND even. - constexpr char kData[] = "The slings and arrows of outrageous fortune,"; - constexpr int kDataSize = sizeof(kData) - 1; - constexpr int kHalfDataSize = kDataSize / 2; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open /dev/zero as write only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY)); - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT( - bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize), - SyscallSucceedsWithValue(kHalfDataSize)); - - char actual[kHalfDataSize]; - // Verify that the input file offset has been updated. - ASSERT_THAT(read(inf.get(), &actual, kDataSize - bytes_sent), - SyscallSucceedsWithValue(kHalfDataSize)); - EXPECT_EQ( - absl::string_view(kData + kDataSize - bytes_sent, kDataSize - bytes_sent), - absl::string_view(actual, kHalfDataSize)); -} - -TEST(SendFileTest, SendAndUpdateFileOffsetFromNonzeroStartingPoint) { - // Create temp files. - // Test input string length must be > 2 AND divisible by 4. - constexpr char kData[] = "The slings and arrows of outrageous fortune,"; - constexpr int kDataSize = sizeof(kData) - 1; - constexpr int kHalfDataSize = kDataSize / 2; - constexpr int kQuarterDataSize = kHalfDataSize / 2; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - FileDescriptor outf; - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Read a quarter of the data from the infile which should update the file - // offset, we don't actually care about the data so it goes into the garbage. - char garbage[kQuarterDataSize]; - ASSERT_THAT(read(inf.get(), &garbage, kQuarterDataSize), - SyscallSucceedsWithValue(kQuarterDataSize)); - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT( - bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize), - SyscallSucceedsWithValue(kHalfDataSize)); - - // Close out_fd to avoid leak. - outf.reset(); - - // Open the output file as read only. - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Verify that the output file has the correct data. - char actual[kHalfDataSize]; - ASSERT_THAT(read(outf.get(), &actual, bytes_sent), - SyscallSucceedsWithValue(kHalfDataSize)); - EXPECT_EQ(absl::string_view(kData + kQuarterDataSize, kHalfDataSize), - absl::string_view(actual, bytes_sent)); - - // Verify that the input file offset has been updated. - ASSERT_THAT(read(inf.get(), &actual, kQuarterDataSize), - SyscallSucceedsWithValue(kQuarterDataSize)); - - EXPECT_EQ( - absl::string_view(kData + kDataSize - kQuarterDataSize, kQuarterDataSize), - absl::string_view(actual, kQuarterDataSize)); -} - -TEST(SendFileTest, SendAndUpdateGivenOffset) { - // Create temp files. - // Test input string length must be >= 4 AND divisible by 4. - constexpr char kData[] = "Or to take Arms against a Sea of troubles,"; - constexpr int kDataSize = sizeof(kData) + 1; - constexpr int kHalfDataSize = kDataSize / 2; - constexpr int kQuarterDataSize = kHalfDataSize / 2; - constexpr int kThreeFourthsDataSize = 3 * kDataSize / 4; - - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - FileDescriptor outf; - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Create offset for sending. - off_t offset = kQuarterDataSize; - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT( - bytes_sent = sendfile(outf.get(), inf.get(), &offset, kHalfDataSize), - SyscallSucceedsWithValue(kHalfDataSize)); - - // Close out_fd to avoid leak. - outf.reset(); - - // Open the output file as read only. - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Verify that the output file has the correct data. - char actual[kHalfDataSize]; - ASSERT_THAT(read(outf.get(), &actual, bytes_sent), - SyscallSucceedsWithValue(kHalfDataSize)); - EXPECT_EQ(absl::string_view(kData + kQuarterDataSize, kHalfDataSize), - absl::string_view(actual, bytes_sent)); - - // Verify that the input file offset has NOT been updated. - ASSERT_THAT(read(inf.get(), &actual, kHalfDataSize), - SyscallSucceedsWithValue(kHalfDataSize)); - EXPECT_EQ(absl::string_view(kData, kHalfDataSize), - absl::string_view(actual, kHalfDataSize)); - - // Verify that the offset pointer has been updated. - EXPECT_EQ(offset, kThreeFourthsDataSize); -} - -TEST(SendFileTest, DoNotSendfileIfOutfileIsAppendOnly) { - // Create temp files. - constexpr char kData[] = "And by opposing end them: to die, to sleep"; - constexpr int kDataSize = sizeof(kData) - 1; - - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as append only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY | O_APPEND)); - - // Send data and verify that sendfile returns the correct errno. - EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SendFileTest, AppendCheckOrdering) { - constexpr char kData[] = "And by opposing end them: to die, to sleep"; - constexpr int kDataSize = sizeof(kData) - 1; - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - - const FileDescriptor read = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - const FileDescriptor write = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY)); - const FileDescriptor append = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_APPEND)); - - // Check that read/write file mode is verified before append. - EXPECT_THAT(sendfile(append.get(), read.get(), nullptr, kDataSize), - SyscallFailsWithErrno(EBADF)); - EXPECT_THAT(sendfile(write.get(), write.get(), nullptr, kDataSize), - SyscallFailsWithErrno(EBADF)); -} - -TEST(SendFileTest, DoNotSendfileIfOutfileIsNotWritable) { - // Create temp files. - constexpr char kData[] = "No more; and by a sleep, to say we end"; - constexpr int kDataSize = sizeof(kData) - 1; - - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as read only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Send data and verify that sendfile returns the correct errno. - EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize), - SyscallFailsWithErrno(EBADF)); -} - -TEST(SendFileTest, DoNotSendfileIfInfileIsNotReadable) { - // Create temp files. - constexpr char kData[] = "the heart-ache, and the thousand natural shocks"; - constexpr int kDataSize = sizeof(kData) - 1; - - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as write only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_WRONLY)); - - // Open the output file as write only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct errno. - EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, kDataSize), - SyscallFailsWithErrno(EBADF)); -} - -TEST(SendFileTest, DoNotSendANegativeNumberOfBytes) { - // Create temp files. - constexpr char kData[] = "that Flesh is heir to? 'Tis a consummation"; - - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct errno. - EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, -1), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SendFileTest, SendTheCorrectNumberOfBytesEvenIfWeTryToSendTooManyBytes) { - // Create temp files. - constexpr char kData[] = "devoutly to be wished. To die, to sleep,"; - constexpr int kDataSize = sizeof(kData) - 1; - - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - FileDescriptor outf; - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Send data and verify that sendfile returns the correct value. - int bytes_sent; - EXPECT_THAT( - bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kDataSize + 100), - SyscallSucceedsWithValue(kDataSize)); - - // Close outf to avoid leak. - outf.reset(); - - // Open the output file as read only. - outf = ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - - // Verify that the output file has the correct data. - char actual[kDataSize]; - ASSERT_THAT(read(outf.get(), &actual, bytes_sent), - SyscallSucceedsWithValue(kDataSize)); - EXPECT_EQ(kData, absl::string_view(actual, bytes_sent)); -} - -TEST(SendFileTest, SendToNotARegularFile) { - // Make temp input directory and open as read only. - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY)); - - // Make temp output file and open as write only. - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Receive an error since a directory is not a regular file. - EXPECT_THAT(sendfile(outf.get(), inf.get(), nullptr, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SendFileTest, SendPipeWouldBlock) { - // Create temp file. - constexpr char kData[] = - "The fool doth think he is wise, but the wise man knows himself to be a " - "fool."; - constexpr int kDataSize = sizeof(kData) - 1; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Setup the output named pipe. - int fds[2]; - ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Fill up the pipe's buffer. - int pipe_size = -1; - ASSERT_THAT(pipe_size = fcntl(wfd.get(), F_GETPIPE_SZ), SyscallSucceeds()); - std::vector<char> buf(2 * pipe_size); - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(pipe_size)); - - EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, kDataSize), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST(SendFileTest, SendPipeEOF) { - // Create and open an empty input file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Setup the output named pipe. - int fds[2]; - ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, 123), - SyscallSucceedsWithValue(0)); -} - -TEST(SendFileTest, SendToFullPipeReturnsEAGAIN) { - // Create and open an empty input file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - - // Set up the output pipe. - int fds[2]; - ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - int pipe_size = -1; - ASSERT_THAT(pipe_size = fcntl(wfd.get(), F_GETPIPE_SZ), SyscallSucceeds()); - int data_size = pipe_size * 8; - ASSERT_THAT(ftruncate(in_fd.get(), data_size), SyscallSucceeds()); - - ASSERT_THAT(sendfile(wfd.get(), in_fd.get(), 0, data_size), - SyscallSucceeds()); - EXPECT_THAT(sendfile(wfd.get(), in_fd.get(), 0, data_size), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(SendFileTest, SendPipeBlocks) { - // Create temp file. - constexpr char kData[] = - "The fault, dear Brutus, is not in our stars, but in ourselves."; - constexpr int kDataSize = sizeof(kData) - 1; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Setup the output named pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Fill up the pipe's buffer. - int pipe_size = -1; - ASSERT_THAT(pipe_size = fcntl(wfd.get(), F_GETPIPE_SZ), SyscallSucceeds()); - std::vector<char> buf(pipe_size); - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(pipe_size)); - - ScopedThread t([&]() { - absl::SleepFor(absl::Milliseconds(100)); - ASSERT_THAT(read(rfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(pipe_size)); - }); - - EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, kDataSize), - SyscallSucceedsWithValue(kDataSize)); -} - -TEST(SendFileTest, SendToSpecialFile) { - // Create temp file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - constexpr int kSize = 0x7ff; - ASSERT_THAT(ftruncate(inf.get(), kSize), SyscallSucceeds()); - - auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD()); - - // eventfd can accept a number of bytes which is a multiple of 8. - EXPECT_THAT(sendfile(eventfd.get(), inf.get(), nullptr, 0xfffff), - SyscallSucceedsWithValue(kSize & (~7))); -} - -TEST(SendFileTest, SendFileToPipe) { - // Create temp file. - constexpr char kData[] = "<insert-quote-here>"; - constexpr int kDataSize = sizeof(kData) - 1; - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode)); - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Create a pipe for sending to a pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Expect to read up to the given size. - std::vector<char> buf(kDataSize); - ScopedThread t([&]() { - absl::SleepFor(absl::Milliseconds(100)); - ASSERT_THAT(read(rfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kDataSize)); - }); - - // Send with twice the size of the file, which should hit EOF. - EXPECT_THAT(sendfile(wfd.get(), inf.get(), nullptr, kDataSize * 2), - SyscallSucceedsWithValue(kDataSize)); -} - -TEST(SendFileTest, SendFileToSelf_NoRandomSave) { - int rawfd; - ASSERT_THAT(rawfd = memfd_create("memfd", 0), SyscallSucceeds()); - const FileDescriptor fd(rawfd); - - char c = 0x01; - ASSERT_THAT(WriteFd(fd.get(), &c, 1), SyscallSucceedsWithValue(1)); - - // Arbitrarily chosen to make sendfile() take long enough that the sentry - // watchdog usually fires unless it's reset by sendfile() between iterations - // of the buffered copy. See b/172076632. - constexpr size_t kSendfileSize = 0xa00000; - - off_t offset = 0; - ASSERT_THAT(sendfile(fd.get(), fd.get(), &offset, kSendfileSize), - SyscallSucceedsWithValue(kSendfileSize)); -} - -static volatile int signaled = 0; -void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; } - -TEST(SendFileTest, ToEventFDDoesNotSpin_NoRandomSave) { - FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); - - // Write the maximum value of an eventfd to a file. - const uint64_t kMaxEventfdValue = 0xfffffffffffffffe; - const auto tempfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const auto tempfd = ASSERT_NO_ERRNO_AND_VALUE(Open(tempfile.path(), O_RDWR)); - ASSERT_THAT( - pwrite(tempfd.get(), &kMaxEventfdValue, sizeof(kMaxEventfdValue), 0), - SyscallSucceedsWithValue(sizeof(kMaxEventfdValue))); - - // Set the eventfd's value to 1. - const uint64_t kOne = 1; - ASSERT_THAT(write(efd.get(), &kOne, sizeof(kOne)), - SyscallSucceedsWithValue(sizeof(kOne))); - - // Set up signal handler. - struct sigaction sa = {}; - sa.sa_sigaction = SigUsr1Handler; - sa.sa_flags = SA_SIGINFO; - const auto cleanup_sigact = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); - - // Send SIGUSR1 to this thread in 1 second. - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = SIGUSR1; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - struct itimerspec its = {}; - its.it_value = absl::ToTimespec(absl::Seconds(1)); - DisableSave ds; // Asserting an EINTR. - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // Sendfile from tempfd to the eventfd. Since the eventfd is not already at - // its maximum value, the eventfd is "ready for writing"; however, since the - // eventfd's existing value plus the new value would exceed the maximum, the - // write should internally fail with EWOULDBLOCK. In this case, sendfile() - // should block instead of spinning, and eventually be interrupted by our - // timer. See b/172075629. - EXPECT_THAT( - sendfile(efd.get(), tempfd.get(), nullptr, sizeof(kMaxEventfdValue)), - SyscallFailsWithErrno(EINTR)); - - // Signal should have been handled. - EXPECT_EQ(signaled, 1); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sendfile_socket.cc b/test/syscalls/linux/sendfile_socket.cc deleted file mode 100644 index c101fe9d2..000000000 --- a/test/syscalls/linux/sendfile_socket.cc +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <netinet/in.h> -#include <sys/sendfile.h> -#include <sys/socket.h> -#include <unistd.h> - -#include <iostream> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { -namespace { - -class SendFileTest : public ::testing::TestWithParam<int> { - protected: - PosixErrorOr<std::unique_ptr<SocketPair>> Sockets(int type) { - // Bind a server socket. - int family = GetParam(); - switch (family) { - case AF_INET: { - if (type == SOCK_STREAM) { - return SocketPairKind{ - "TCP", AF_INET, type, 0, - TCPAcceptBindSocketPairCreator(AF_INET, type, 0, false)} - .Create(); - } else { - return SocketPairKind{ - "UDP", AF_INET, type, 0, - UDPBidirectionalBindSocketPairCreator(AF_INET, type, 0, false)} - .Create(); - } - } - case AF_UNIX: { - if (type == SOCK_STREAM) { - return SocketPairKind{ - "UNIX", AF_UNIX, type, 0, - FilesystemAcceptBindSocketPairCreator(AF_UNIX, type, 0)} - .Create(); - } else { - return SocketPairKind{ - "UNIX", AF_UNIX, type, 0, - FilesystemBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)} - .Create(); - } - } - default: - return PosixError(EINVAL); - } - } -}; - -// Sends large file to exercise the path that read and writes data multiple -// times, esp. when more data is read than can be written. -TEST_P(SendFileTest, SendMultiple) { - std::vector<char> data(5 * 1024 * 1024); - RandomizeBuffer(data.data(), data.size()); - - // Create temp files. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::string_view(data.data(), data.size()), - TempPath::kDefaultFileMode)); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Create sockets. - auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_STREAM)); - - // Thread that reads data from socket and dumps to a file. - ScopedThread th([&] { - FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Read until socket is closed. - char buf[10240]; - for (int cnt = 0;; cnt++) { - int r = RetryEINTR(read)(socks->first_fd(), buf, sizeof(buf)); - // We cannot afford to save on every read() call. - if (cnt % 1000 == 0) { - ASSERT_THAT(r, SyscallSucceeds()); - } else { - const DisableSave ds; - ASSERT_THAT(r, SyscallSucceeds()); - } - if (r == 0) { - // EOF - break; - } - int w = RetryEINTR(write)(outf.get(), buf, r); - // We cannot afford to save on every write() call. - if (cnt % 1010 == 0) { - ASSERT_THAT(w, SyscallSucceedsWithValue(r)); - } else { - const DisableSave ds; - ASSERT_THAT(w, SyscallSucceedsWithValue(r)); - } - } - }); - - // Open the input file as read only. - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - int cnt = 0; - for (size_t sent = 0; sent < data.size(); cnt++) { - const size_t remain = data.size() - sent; - std::cout << "sendfile, size=" << data.size() << ", sent=" << sent - << ", remain=" << remain << std::endl; - - // Send data and verify that sendfile returns the correct value. - int res = sendfile(socks->second_fd(), inf.get(), nullptr, remain); - // We cannot afford to save on every sendfile() call. - if (cnt % 120 == 0) { - MaybeSave(); - } - if (res == 0) { - // EOF - break; - } - if (res > 0) { - sent += res; - } else { - ASSERT_TRUE(errno == EINTR || errno == EAGAIN) << "errno=" << errno; - } - } - - // Close socket to stop thread. - close(socks->release_second_fd()); - th.Join(); - - // Verify that the output file has the correct data. - const FileDescriptor outf = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDONLY)); - std::vector<char> actual(data.size(), '\0'); - ASSERT_THAT(RetryEINTR(read)(outf.get(), actual.data(), actual.size()), - SyscallSucceedsWithValue(actual.size())); - ASSERT_EQ(memcmp(data.data(), actual.data(), data.size()), 0); -} - -TEST_P(SendFileTest, Shutdown) { - // Create a socket. - auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_STREAM)); - - // If this is a TCP socket, then turn off linger. - if (GetParam() == AF_INET) { - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = 0; - ASSERT_THAT( - setsockopt(socks->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - } - - // Create a 1m file with random data. - std::vector<char> data(1024 * 1024); - RandomizeBuffer(data.data(), data.size()); - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::string_view(data.data(), data.size()), - TempPath::kDefaultFileMode)); - const FileDescriptor inf = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Read some data, then shutdown the socket. We don't actually care about - // checking the contents (other tests do that), so we just re-use the same - // buffer as above. - ScopedThread t([&]() { - size_t done = 0; - while (done < data.size()) { - int n = RetryEINTR(read)(socks->first_fd(), data.data(), data.size()); - ASSERT_THAT(n, SyscallSucceeds()); - done += n; - } - // Close the server side socket. - close(socks->release_first_fd()); - }); - - // Continuously stream from the file to the socket. Note we do not assert - // that a specific amount of data has been written at any time, just that some - // data is written. Eventually, we should get a connection reset error. - while (1) { - off_t offset = 0; // Always read from the start. - int n = sendfile(socks->second_fd(), inf.get(), &offset, data.size()); - EXPECT_THAT(n, AnyOf(SyscallFailsWithErrno(ECONNRESET), - SyscallFailsWithErrno(EPIPE), SyscallSucceeds())); - if (n <= 0) { - break; - } - } -} - -TEST_P(SendFileTest, SendpageFromEmptyFileToUDP) { - auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_DGRAM)); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - - // The value to the count argument has to be so that it is impossible to - // allocate a buffer of this size. In Linux, sendfile transfer at most - // 0x7ffff000 (MAX_RW_COUNT) bytes. - EXPECT_THAT(sendfile(socks->first_fd(), fd.get(), 0x0, 0x8000000000004), - SyscallSucceedsWithValue(0)); -} - -INSTANTIATE_TEST_SUITE_P(AddressFamily, SendFileTest, - ::testing::Values(AF_UNIX, AF_INET)); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/setgid.cc b/test/syscalls/linux/setgid.cc deleted file mode 100644 index 163242ace..000000000 --- a/test/syscalls/linux/setgid.cc +++ /dev/null @@ -1,413 +0,0 @@ -// 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 <limits.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -ABSL_FLAG(std::vector<std::string>, groups, std::vector<std::string>({}), - "groups the test can use"); - -constexpr gid_t kNobody = 65534; - -namespace gvisor { -namespace testing { - -namespace { - -constexpr int kDirmodeMask = 07777; -constexpr int kDirmodeSgid = S_ISGID | 0777; -constexpr int kDirmodeNoExec = S_ISGID | 0767; -constexpr int kDirmodeNoSgid = 0777; - -// Sets effective GID and returns a Cleanup that restores the original. -PosixErrorOr<Cleanup> Setegid(gid_t egid) { - gid_t old_gid = getegid(); - if (setegid(egid) < 0) { - return PosixError(errno, absl::StrFormat("setegid(%d)", egid)); - } - return Cleanup( - [old_gid]() { EXPECT_THAT(setegid(old_gid), SyscallSucceeds()); }); -} - -// Returns a pair of groups that the user is a member of. -PosixErrorOr<std::pair<gid_t, gid_t>> Groups() { - // Were we explicitly passed GIDs? - std::vector<std::string> flagged_groups = absl::GetFlag(FLAGS_groups); - if (flagged_groups.size() >= 2) { - int group1; - int group2; - if (!absl::SimpleAtoi(flagged_groups[0], &group1) || - !absl::SimpleAtoi(flagged_groups[1], &group2)) { - return PosixError(EINVAL, "failed converting group flags to ints"); - } - return std::pair<gid_t, gid_t>(group1, group2); - } - - // See whether the user is a member of at least 2 groups. - std::vector<gid_t> groups(64); - for (; groups.size() <= NGROUPS_MAX; groups.resize(groups.size() * 2)) { - int ngroups = getgroups(groups.size(), groups.data()); - if (ngroups < 0 && errno == EINVAL) { - // Need a larger list. - continue; - } - if (ngroups < 0) { - return PosixError(errno, absl::StrFormat("getgroups(%d, %p)", - groups.size(), groups.data())); - } - - if (ngroups < 2) { - // There aren't enough groups. - break; - } - - // TODO(b/181878080): Read /proc/sys/fs/overflowgid once it is supported in - // gVisor. - if (groups[0] == kNobody || groups[1] == kNobody) { - // These groups aren't mapped into our user namespace, so we can't use - // them. - break; - } - return std::pair<gid_t, gid_t>(groups[0], groups[1]); - } - - // If we're running in gVisor and are root in the root user namespace, we can - // set our GID to whatever we want. Try that before giving up. - // - // This won't work in native tests, as despite having CAP_SETGID, the gofer - // process will be sandboxed and unable to change file GIDs. - if (!IsRunningOnGvisor()) { - return PosixError(EPERM, "no valid groups for native testing"); - } - PosixErrorOr<bool> capable = HaveCapability(CAP_SETGID); - if (!capable.ok()) { - return capable.error(); - } - if (!capable.ValueOrDie()) { - return PosixError(EPERM, "missing CAP_SETGID"); - } - gid_t gid = getegid(); - auto cleanup1 = Setegid(gid); - if (!cleanup1.ok()) { - return cleanup1.error(); - } - auto cleanup2 = Setegid(kNobody); - if (!cleanup2.ok()) { - return cleanup2.error(); - } - return std::pair<gid_t, gid_t>(gid, kNobody); -} - -class SetgidDirTest : public ::testing::Test { - protected: - void SetUp() override { - original_gid_ = getegid(); - - SKIP_IF(IsRunningWithVFS1()); - - temp_dir_ = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0777 /* mode */)); - - // If we can't find two usable groups, we're in an unsupporting environment. - // Skip the test. - PosixErrorOr<std::pair<gid_t, gid_t>> groups = Groups(); - SKIP_IF(!groups.ok()); - groups_ = groups.ValueOrDie(); - } - - void TearDown() override { - EXPECT_THAT(setegid(original_gid_), SyscallSucceeds()); - } - - void MkdirAsGid(gid_t gid, const std::string& path, mode_t mode) { - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(Setegid(gid)); - ASSERT_THAT(mkdir(path.c_str(), mode), SyscallSucceeds()); - } - - PosixErrorOr<struct stat> Stat(const std::string& path) { - struct stat stats; - if (stat(path.c_str(), &stats) < 0) { - return PosixError(errno, absl::StrFormat("stat(%s, _)", path)); - } - return stats; - } - - PosixErrorOr<struct stat> Stat(const FileDescriptor& fd) { - struct stat stats; - if (fstat(fd.get(), &stats) < 0) { - return PosixError(errno, "fstat(_, _)"); - } - return stats; - } - - TempPath temp_dir_; - std::pair<gid_t, gid_t> groups_; - gid_t original_gid_; -}; - -// The control test. Files created with a given GID are owned by that group. -TEST_F(SetgidDirTest, Control) { - // Set group to G1 and create a directory. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, 0777)); - - // Set group to G2, create a file in g1owned, and confirm that G2 owns it. - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(Setegid(groups_.second)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(g1owned, "g2owned").c_str(), O_CREAT | O_RDWR, 0777)); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.second); -} - -// Setgid directories cause created files to inherit GID. -TEST_F(SetgidDirTest, CreateFile) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeSgid)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeSgid), SyscallSucceeds()); - - // Set group to G2, create a file, and confirm that G1 owns it. - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(Setegid(groups_.second)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(g1owned, "g2created").c_str(), O_CREAT | O_RDWR, 0666)); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.first); -} - -// Setgid directories cause created directories to inherit GID. -TEST_F(SetgidDirTest, CreateDir) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeSgid)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeSgid), SyscallSucceeds()); - - // Set group to G2, create a directory, confirm that G1 owns it, and that the - // setgid bit is enabled. - auto g2created = JoinPath(g1owned, "g2created"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.second, g2created, 0666)); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(g2created)); - EXPECT_EQ(stats.st_gid, groups_.first); - EXPECT_EQ(stats.st_mode & S_ISGID, S_ISGID); -} - -// Setgid directories with group execution disabled still cause GID inheritance. -TEST_F(SetgidDirTest, NoGroupExec) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeNoExec)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeNoExec), SyscallSucceeds()); - - // Set group to G2, create a directory, confirm that G2 owns it, and that the - // setgid bit is enabled. - auto g2created = JoinPath(g1owned, "g2created"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.second, g2created, 0666)); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(g2created)); - EXPECT_EQ(stats.st_gid, groups_.first); - EXPECT_EQ(stats.st_mode & S_ISGID, S_ISGID); -} - -// Setting the setgid bit on directories with an existing file does not change -// the file's group. -TEST_F(SetgidDirTest, OldFile) { - // Set group to G1 and create a directory. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeNoSgid)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeNoSgid), SyscallSucceeds()); - - // Set group to G2, create a file, confirm that G2 owns it. - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(Setegid(groups_.second)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(g1owned, "g2created").c_str(), O_CREAT | O_RDWR, 0666)); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.second); - - // Enable setgid. - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeSgid), SyscallSucceeds()); - - // Confirm that the file's group is still G2. - stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.second); -} - -// Setting the setgid bit on directories with an existing subdirectory does not -// change the subdirectory's group. -TEST_F(SetgidDirTest, OldDir) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeNoSgid)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeNoSgid), SyscallSucceeds()); - - // Set group to G2, create a directory, confirm that G2 owns it. - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(Setegid(groups_.second)); - auto g2created = JoinPath(g1owned, "g2created"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.second, g2created, 0666)); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(g2created)); - EXPECT_EQ(stats.st_gid, groups_.second); - - // Enable setgid. - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeSgid), SyscallSucceeds()); - - // Confirm that the file's group is still G2. - stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(g2created)); - EXPECT_EQ(stats.st_gid, groups_.second); -} - -// Chowning a file clears the setgid and setuid bits. -TEST_F(SetgidDirTest, ChownFileClears) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeMask)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeMask), SyscallSucceeds()); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(g1owned, "newfile").c_str(), O_CREAT | O_RDWR, 0666)); - ASSERT_THAT(fchmod(fd.get(), 0777 | S_ISUID | S_ISGID), SyscallSucceeds()); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.first); - EXPECT_EQ(stats.st_mode & (S_ISUID | S_ISGID), S_ISUID | S_ISGID); - - // Change the owning group. - ASSERT_THAT(fchown(fd.get(), -1, groups_.second), SyscallSucceeds()); - - // The setgid and setuid bits should be cleared. - stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.second); - EXPECT_EQ(stats.st_mode & (S_ISUID | S_ISGID), 0); -} - -// Chowning a file with setgid enabled, but not the group exec bit, does not -// clear the setgid bit. Such files are mandatory locked. -TEST_F(SetgidDirTest, ChownNoExecFileDoesNotClear) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeNoExec)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeNoExec), SyscallSucceeds()); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Open(JoinPath(g1owned, "newdir").c_str(), O_CREAT | O_RDWR, 0666)); - ASSERT_THAT(fchmod(fd.get(), 0766 | S_ISUID | S_ISGID), SyscallSucceeds()); - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.first); - EXPECT_EQ(stats.st_mode & (S_ISUID | S_ISGID), S_ISUID | S_ISGID); - - // Change the owning group. - ASSERT_THAT(fchown(fd.get(), -1, groups_.second), SyscallSucceeds()); - - // Only the setuid bit is cleared. - stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(fd)); - EXPECT_EQ(stats.st_gid, groups_.second); - EXPECT_EQ(stats.st_mode & (S_ISUID | S_ISGID), S_ISGID); -} - -// Chowning a directory with setgid enabled does not clear the bit. -TEST_F(SetgidDirTest, ChownDirDoesNotClear) { - // Set group to G1, create a directory, and enable setgid. - auto g1owned = JoinPath(temp_dir_.path(), "g1owned/"); - ASSERT_NO_FATAL_FAILURE(MkdirAsGid(groups_.first, g1owned, kDirmodeMask)); - ASSERT_THAT(chmod(g1owned.c_str(), kDirmodeMask), SyscallSucceeds()); - - // Change the owning group. - ASSERT_THAT(chown(g1owned.c_str(), -1, groups_.second), SyscallSucceeds()); - - struct stat stats = ASSERT_NO_ERRNO_AND_VALUE(Stat(g1owned)); - EXPECT_EQ(stats.st_gid, groups_.second); - EXPECT_EQ(stats.st_mode & kDirmodeMask, kDirmodeMask); -} - -struct FileModeTestcase { - std::string name; - mode_t mode; - mode_t result_mode; - - FileModeTestcase(const std::string& name, mode_t mode, mode_t result_mode) - : name(name), mode(mode), result_mode(result_mode) {} -}; - -class FileModeTest : public ::testing::TestWithParam<FileModeTestcase> {}; - -TEST_P(FileModeTest, WriteToFile) { - SKIP_IF(IsRunningWithVFS1()); - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0777 /* mode */)); - auto path = JoinPath(temp_dir.path(), GetParam().name); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.c_str(), O_CREAT | O_RDWR, 0666)); - ASSERT_THAT(fchmod(fd.get(), GetParam().mode), SyscallSucceeds()); - struct stat stats; - ASSERT_THAT(fstat(fd.get(), &stats), SyscallSucceeds()); - EXPECT_EQ(stats.st_mode & kDirmodeMask, GetParam().mode); - - // For security reasons, writing to the file clears the SUID bit, and clears - // the SGID bit when the group executable bit is unset (which is not a true - // SGID binary). - constexpr char kInput = 'M'; - ASSERT_THAT(write(fd.get(), &kInput, sizeof(kInput)), - SyscallSucceedsWithValue(sizeof(kInput))); - - ASSERT_THAT(fstat(fd.get(), &stats), SyscallSucceeds()); - EXPECT_EQ(stats.st_mode & kDirmodeMask, GetParam().result_mode); -} - -TEST_P(FileModeTest, TruncateFile) { - SKIP_IF(IsRunningWithVFS1()); - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0777 /* mode */)); - auto path = JoinPath(temp_dir.path(), GetParam().name); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path.c_str(), O_CREAT | O_RDWR, 0666)); - ASSERT_THAT(fchmod(fd.get(), GetParam().mode), SyscallSucceeds()); - struct stat stats; - ASSERT_THAT(fstat(fd.get(), &stats), SyscallSucceeds()); - EXPECT_EQ(stats.st_mode & kDirmodeMask, GetParam().mode); - - // Write something to the file, as truncating an empty file is a no-op. - constexpr char c = 'M'; - ASSERT_THAT(write(fd.get(), &c, sizeof(c)), - SyscallSucceedsWithValue(sizeof(c))); - - // For security reasons, truncating the file clears the SUID bit, and clears - // the SGID bit when the group executable bit is unset (which is not a true - // SGID binary). - ASSERT_THAT(ftruncate(fd.get(), 0), SyscallSucceeds()); - - ASSERT_THAT(fstat(fd.get(), &stats), SyscallSucceeds()); - EXPECT_EQ(stats.st_mode & kDirmodeMask, GetParam().result_mode); -} - -INSTANTIATE_TEST_SUITE_P( - FileModes, FileModeTest, - ::testing::ValuesIn<FileModeTestcase>( - {FileModeTestcase("normal file", 0777, 0777), - FileModeTestcase("setuid", S_ISUID | 0777, 00777), - FileModeTestcase("setgid", S_ISGID | 0777, 00777), - FileModeTestcase("setuid and setgid", S_ISUID | S_ISGID | 0777, 00777), - FileModeTestcase("setgid without exec", S_ISGID | 0767, - S_ISGID | 0767), - FileModeTestcase("setuid and setgid without exec", - S_ISGID | S_ISUID | 0767, S_ISGID | 0767)})); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/shm.cc b/test/syscalls/linux/shm.cc deleted file mode 100644 index baf794152..000000000 --- a/test/syscalls/linux/shm.cc +++ /dev/null @@ -1,505 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/ipc.h> -#include <sys/mman.h> -#include <sys/shm.h> -#include <sys/types.h> - -#include "absl/time/clock.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -using ::testing::_; -using ::testing::AnyOf; -using ::testing::Eq; - -const uint64_t kAllocSize = kPageSize * 128ULL; - -PosixErrorOr<char*> Shmat(int shmid, const void* shmaddr, int shmflg) { - const intptr_t addr = - reinterpret_cast<intptr_t>(shmat(shmid, shmaddr, shmflg)); - if (addr == -1) { - return PosixError(errno, "shmat() failed"); - } - return reinterpret_cast<char*>(addr); -} - -PosixError Shmdt(const char* shmaddr) { - const int ret = shmdt(shmaddr); - if (ret == -1) { - return PosixError(errno, "shmdt() failed"); - } - return NoError(); -} - -template <typename T> -PosixErrorOr<int> Shmctl(int shmid, int cmd, T* buf) { - int ret = shmctl(shmid, cmd, reinterpret_cast<struct shmid_ds*>(buf)); - if (ret == -1) { - return PosixError(errno, "shmctl() failed"); - } - return ret; -} - -// ShmSegment is a RAII object for automatically cleaning up shm segments. -class ShmSegment { - public: - explicit ShmSegment(int id) : id_(id) {} - - ~ShmSegment() { - if (id_ >= 0) { - EXPECT_NO_ERRNO(Rmid()); - id_ = -1; - } - } - - ShmSegment(ShmSegment&& other) : id_(other.release()) {} - - ShmSegment& operator=(ShmSegment&& other) { - id_ = other.release(); - return *this; - } - - ShmSegment(ShmSegment const& other) = delete; - ShmSegment& operator=(ShmSegment const& other) = delete; - - int id() const { return id_; } - - int release() { - int id = id_; - id_ = -1; - return id; - } - - PosixErrorOr<int> Rmid() { - RETURN_IF_ERRNO(Shmctl<void>(id_, IPC_RMID, nullptr)); - return release(); - } - - private: - int id_ = -1; -}; - -PosixErrorOr<int> ShmgetRaw(key_t key, size_t size, int shmflg) { - int id = shmget(key, size, shmflg); - if (id == -1) { - return PosixError(errno, "shmget() failed"); - } - return id; -} - -PosixErrorOr<ShmSegment> Shmget(key_t key, size_t size, int shmflg) { - ASSIGN_OR_RETURN_ERRNO(int id, ShmgetRaw(key, size, shmflg)); - return ShmSegment(id); -} - -TEST(ShmTest, AttachDetach) { - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - struct shmid_ds attr; - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - EXPECT_EQ(attr.shm_segsz, kAllocSize); - EXPECT_EQ(attr.shm_nattch, 0); - - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - EXPECT_EQ(attr.shm_nattch, 1); - - const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - EXPECT_EQ(attr.shm_nattch, 2); - - ASSERT_NO_ERRNO(Shmdt(addr)); - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - EXPECT_EQ(attr.shm_nattch, 1); - - ASSERT_NO_ERRNO(Shmdt(addr2)); - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - EXPECT_EQ(attr.shm_nattch, 0); -} - -TEST(ShmTest, LookupByKey) { - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const key_t key = ftok(keyfile.path().c_str(), 1); - const ShmSegment shm = - ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); - const int id2 = ASSERT_NO_ERRNO_AND_VALUE(ShmgetRaw(key, kAllocSize, 0777)); - EXPECT_EQ(shm.id(), id2); -} - -TEST(ShmTest, DetachedSegmentsPersist) { - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - addr[0] = 'x'; - ASSERT_NO_ERRNO(Shmdt(addr)); - - // We should be able to re-attach to the same segment and get our data back. - addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - EXPECT_EQ(addr[0], 'x'); - ASSERT_NO_ERRNO(Shmdt(addr)); -} - -TEST(ShmTest, MultipleDetachFails) { - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - ASSERT_NO_ERRNO(Shmdt(addr)); - EXPECT_THAT(Shmdt(addr), PosixErrorIs(EINVAL, _)); -} - -TEST(ShmTest, IpcStat) { - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const key_t key = ftok(keyfile.path().c_str(), 1); - - const time_t start = time(nullptr); - - const ShmSegment shm = - ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); - - const uid_t uid = getuid(); - const gid_t gid = getgid(); - const pid_t pid = getpid(); - - struct shmid_ds attr; - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - - EXPECT_EQ(attr.shm_perm.__key, key); - EXPECT_EQ(attr.shm_perm.uid, uid); - EXPECT_EQ(attr.shm_perm.gid, gid); - EXPECT_EQ(attr.shm_perm.cuid, uid); - EXPECT_EQ(attr.shm_perm.cgid, gid); - EXPECT_EQ(attr.shm_perm.mode, 0777); - - EXPECT_EQ(attr.shm_segsz, kAllocSize); - - EXPECT_EQ(attr.shm_atime, 0); - EXPECT_EQ(attr.shm_dtime, 0); - - // Change time is set on creation. - EXPECT_GE(attr.shm_ctime, start); - - EXPECT_EQ(attr.shm_cpid, pid); - EXPECT_EQ(attr.shm_lpid, 0); - - EXPECT_EQ(attr.shm_nattch, 0); - - // The timestamps only have a resolution of seconds; slow down so we actually - // see the timestamps change. - absl::SleepFor(absl::Seconds(1)); - const time_t pre_attach = time(nullptr); - - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - - EXPECT_GE(attr.shm_atime, pre_attach); - EXPECT_EQ(attr.shm_dtime, 0); - EXPECT_LT(attr.shm_ctime, pre_attach); - EXPECT_EQ(attr.shm_lpid, pid); - EXPECT_EQ(attr.shm_nattch, 1); - - absl::SleepFor(absl::Seconds(1)); - const time_t pre_detach = time(nullptr); - - ASSERT_NO_ERRNO(Shmdt(addr)); - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - - EXPECT_LT(attr.shm_atime, pre_detach); - EXPECT_GE(attr.shm_dtime, pre_detach); - EXPECT_LT(attr.shm_ctime, pre_detach); - EXPECT_EQ(attr.shm_lpid, pid); - EXPECT_EQ(attr.shm_nattch, 0); -} - -TEST(ShmTest, ShmStat) { - // This test relies on the segment we create to be the first one on the - // system, causing it to occupy slot 1. We can't reasonably expect this on a - // general Linux host. - SKIP_IF(!IsRunningOnGvisor()); - - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - struct shmid_ds attr; - ASSERT_NO_ERRNO(Shmctl(1, SHM_STAT, &attr)); - // This does the same thing as IPC_STAT, so only test that the syscall - // succeeds here. -} - -TEST(ShmTest, IpcInfo) { - struct shminfo info; - ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); - - EXPECT_EQ(info.shmmin, 1); // This is always 1, according to the man page. - EXPECT_GT(info.shmmax, info.shmmin); - EXPECT_GT(info.shmmni, 0); - EXPECT_GT(info.shmseg, 0); - EXPECT_GT(info.shmall, 0); -} - -TEST(ShmTest, ShmInfo) { - // Take a snapshot of the system before the test runs. - struct shm_info snap; - ASSERT_NO_ERRNO(Shmctl(0, SHM_INFO, &snap)); - - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - - struct shm_info info; - ASSERT_NO_ERRNO(Shmctl(1, SHM_INFO, &info)); - - // We generally can't know what other processes on a linux machine do with - // shared memory segments, so we can't test specific numbers on Linux. When - // running under gvisor, we're guaranteed to be the only ones using shm, so - // we can easily verify machine-wide numbers. - if (IsRunningOnGvisor()) { - ASSERT_NO_ERRNO(Shmctl(shm.id(), SHM_INFO, &info)); - EXPECT_EQ(info.used_ids, snap.used_ids + 1); - EXPECT_EQ(info.shm_tot, snap.shm_tot + (kAllocSize / kPageSize)); - EXPECT_EQ(info.shm_rss, snap.shm_rss + (kAllocSize / kPageSize)); - EXPECT_EQ(info.shm_swp, 0); // Gvisor currently never swaps. - } - - ASSERT_NO_ERRNO(Shmdt(addr)); -} - -TEST(ShmTest, ShmCtlSet) { - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - - struct shmid_ds attr; - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - ASSERT_EQ(attr.shm_perm.mode, 0777); - - attr.shm_perm.mode = 0766; - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_SET, &attr)); - - ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); - ASSERT_EQ(attr.shm_perm.mode, 0766); - - ASSERT_NO_ERRNO(Shmdt(addr)); -} - -TEST(ShmTest, RemovedSegmentsAreMarkedDeleted) { - ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - const int id = ASSERT_NO_ERRNO_AND_VALUE(shm.Rmid()); - struct shmid_ds attr; - ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); - EXPECT_NE(attr.shm_perm.mode & SHM_DEST, 0); - ASSERT_NO_ERRNO(Shmdt(addr)); -} - -TEST(ShmTest, RemovedSegmentsAreDestroyed) { - ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - - const uint64_t alloc_pages = kAllocSize / kPageSize; - - struct shm_info info; - ASSERT_NO_ERRNO(Shmctl(0 /*ignored*/, SHM_INFO, &info)); - const uint64_t before = info.shm_tot; - - ASSERT_NO_ERRNO(shm.Rmid()); - ASSERT_NO_ERRNO(Shmdt(addr)); - - ASSERT_NO_ERRNO(Shmctl(0 /*ignored*/, SHM_INFO, &info)); - if (IsRunningOnGvisor()) { - // No guarantees on system-wide shm memory usage on a generic linux host. - const uint64_t after = info.shm_tot; - EXPECT_EQ(after, before - alloc_pages); - } -} - -TEST(ShmTest, AllowsAttachToRemovedSegmentWithRefs) { - ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - const int id = ASSERT_NO_ERRNO_AND_VALUE(shm.Rmid()); - const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); - ASSERT_NO_ERRNO(Shmdt(addr)); - ASSERT_NO_ERRNO(Shmdt(addr2)); -} - -TEST(ShmTest, RemovedSegmentsAreNotDiscoverable) { - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const key_t key = ftok(keyfile.path().c_str(), 1); - ShmSegment shm = - ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); - ASSERT_NO_ERRNO(shm.Rmid()); - EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); -} - -TEST(ShmDeathTest, ReadonlySegment) { - SetupGvisorDeathTest(); - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, SHM_RDONLY)); - // Reading succeeds. - static_cast<void>(addr[0]); - // Writing fails. - EXPECT_EXIT(addr[0] = 'x', ::testing::KilledBySignal(SIGSEGV), ""); -} - -TEST(ShmDeathTest, SegmentNotAccessibleAfterDetach) { - // This test is susceptible to races with concurrent mmaps running in parallel - // gtest threads since the test relies on the address freed during a shm - // segment destruction to remain unused. We run the test body in a forked - // child to guarantee a single-threaded context to avoid this. - - SetupGvisorDeathTest(); - - const auto rest = [&] { - ShmSegment shm = TEST_CHECK_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - char* addr = TEST_CHECK_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - - // Mark the segment as destroyed so it's automatically cleaned up when we - // crash below. We can't rely on the standard cleanup since the destructor - // will not run after the SIGSEGV. Note that this doesn't destroy the - // segment immediately since we're still attached to it. - TEST_CHECK_NO_ERRNO(shm.Rmid()); - - addr[0] = 'x'; - TEST_CHECK_NO_ERRNO(Shmdt(addr)); - - // This access should cause a SIGSEGV. - addr[0] = 'x'; - }; - - EXPECT_THAT(InForkedProcess(rest), - IsPosixErrorOkAndHolds(AnyOf(Eq(W_EXITCODE(0, SIGSEGV)), - Eq(W_EXITCODE(0, 128 + SIGSEGV))))); -} - -TEST(ShmTest, RequestingSegmentSmallerThanSHMMINFails) { - struct shminfo info; - ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); - const uint64_t size = info.shmmin - 1; - EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), - PosixErrorIs(EINVAL, _)); -} - -TEST(ShmTest, RequestingSegmentLargerThanSHMMAXFails) { - struct shminfo info; - ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); - const uint64_t size = info.shmmax + kPageSize; - EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), - PosixErrorIs(EINVAL, _)); -} - -TEST(ShmTest, RequestingUnalignedSizeSucceeds) { - EXPECT_NO_ERRNO(Shmget(IPC_PRIVATE, 4097, IPC_CREAT | 0777)); -} - -TEST(ShmTest, RequestingDuplicateCreationFails) { - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const key_t key = ftok(keyfile.path().c_str(), 1); - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777)); - EXPECT_THAT(Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777), - PosixErrorIs(EEXIST, _)); -} - -TEST(ShmTest, NonExistentSegmentsAreNotFound) { - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const key_t key = ftok(keyfile.path().c_str(), 1); - // Do not request creation. - EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); -} - -TEST(ShmTest, SegmentsSizeFixedOnCreation) { - const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const key_t key = ftok(keyfile.path().c_str(), 1); - - // Base segment. - const ShmSegment shm = - ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); - - // Ask for the same segment at half size. This succeeds. - const int id2 = - ASSERT_NO_ERRNO_AND_VALUE(ShmgetRaw(key, kAllocSize / 2, 0777)); - - // Ask for the same segment at double size. - EXPECT_THAT(Shmget(key, kAllocSize * 2, 0777), PosixErrorIs(EINVAL, _)); - - char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id2, nullptr, 0)); - - // We have 2 different maps... - EXPECT_NE(addr, addr2); - - // ... And both maps are kAllocSize bytes; despite asking for a half-sized - // segment for the second map. - addr[kAllocSize - 1] = 'x'; - addr2[kAllocSize - 1] = 'x'; - - ASSERT_NO_ERRNO(Shmdt(addr)); - ASSERT_NO_ERRNO(Shmdt(addr2)); -} - -TEST(ShmTest, PartialUnmap) { - const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - EXPECT_THAT(munmap(addr + (kAllocSize / 4), kAllocSize / 2), - SyscallSucceeds()); - ASSERT_NO_ERRNO(Shmdt(addr)); -} - -// Check that sentry does not panic when asked for a zero-length private shm -// segment. Regression test for b/110694797. -TEST(ShmTest, GracefullyFailOnZeroLenSegmentCreation) { - EXPECT_THAT(Shmget(IPC_PRIVATE, 0, 0), PosixErrorIs(EINVAL, _)); -} - -TEST(ShmTest, NoDestructionOfAttachedSegmentWithMultipleRmid) { - ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( - Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); - char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); - - // There should be 2 refs to the segment from the 2 attachments, and a single - // self-reference. Mark the segment as destroyed more than 3 times through - // shmctl(RMID). If there's a bug with the ref counting, this should cause the - // count to drop to zero. - int id = shm.release(); - for (int i = 0; i < 6; ++i) { - ASSERT_NO_ERRNO(Shmctl<void>(id, IPC_RMID, nullptr)); - } - - // Segment should remain accessible. - addr[0] = 'x'; - ASSERT_NO_ERRNO(Shmdt(addr)); - - // Segment should remain accessible even after one of the two attachments are - // detached. - addr2[0] = 'x'; - ASSERT_NO_ERRNO(Shmdt(addr2)); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sigaction.cc b/test/syscalls/linux/sigaction.cc deleted file mode 100644 index 9d9dd57a8..000000000 --- a/test/syscalls/linux/sigaction.cc +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/syscall.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SigactionTest, GetLessThanOrEqualToZeroFails) { - 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 = {}; - ASSERT_THAT(sigaction(0, &act, nullptr), SyscallFailsWithErrno(EINVAL)); - ASSERT_THAT(sigaction(0, &act, nullptr), SyscallFailsWithErrno(EINVAL)); -} - -TEST(SigactionTest, GetGreaterThanMaxFails) { - struct sigaction act = {}; - ASSERT_THAT(sigaction(SIGRTMAX + 1, nullptr, &act), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SigactionTest, SetGreaterThanMaxFails) { - struct sigaction act = {}; - ASSERT_THAT(sigaction(SIGRTMAX + 1, &act, nullptr), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SigactionTest, SetSigkillFails) { - struct sigaction act = {}; - ASSERT_THAT(sigaction(SIGKILL, nullptr, &act), SyscallSucceeds()); - ASSERT_THAT(sigaction(SIGKILL, &act, nullptr), SyscallFailsWithErrno(EINVAL)); -} - -TEST(SigactionTest, SetSigstopFails) { - 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 - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc deleted file mode 100644 index 24e7c4960..000000000 --- a/test/syscalls/linux/sigaltstack.cc +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include <functional> -#include <vector> - -#include "gtest/gtest.h" -#include "test/util/cleanup.h" -#include "test/util/fs_util.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -PosixErrorOr<Cleanup> ScopedSigaltstack(stack_t const& stack) { - stack_t old_stack; - int rc = sigaltstack(&stack, &old_stack); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "sigaltstack failed"); - } - return Cleanup([old_stack] { - EXPECT_THAT(sigaltstack(&old_stack, nullptr), SyscallSucceeds()); - }); -} - -volatile bool got_signal = false; -volatile int sigaltstack_errno = 0; -volatile int ss_flags = 0; - -void sigaltstack_handler(int sig, siginfo_t* siginfo, void* arg) { - got_signal = true; - - stack_t stack; - int ret = sigaltstack(nullptr, &stack); - MaybeSave(); - if (ret < 0) { - sigaltstack_errno = errno; - return; - } - ss_flags = stack.ss_flags; -} - -TEST(SigaltstackTest, Success) { - std::vector<char> stack_mem(SIGSTKSZ); - stack_t stack = {}; - stack.ss_sp = stack_mem.data(); - stack.ss_size = stack_mem.size(); - auto const cleanup_sigstack = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); - - struct sigaction sa = {}; - sa.sa_sigaction = sigaltstack_handler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO | SA_ONSTACK; - auto const cleanup_sa = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); - - // Send signal to this thread, as sigaltstack is per-thread. - EXPECT_THAT(tgkill(getpid(), gettid(), SIGUSR1), SyscallSucceeds()); - - EXPECT_TRUE(got_signal); - EXPECT_EQ(sigaltstack_errno, 0); - EXPECT_NE(0, ss_flags & SS_ONSTACK); -} - -TEST(SigaltstackTest, ResetByExecve) { - std::vector<char> stack_mem(SIGSTKSZ); - stack_t stack = {}; - stack.ss_sp = stack_mem.data(); - stack.ss_size = stack_mem.size(); - auto const cleanup_sigstack = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); - - std::string full_path = RunfilePath("test/syscalls/linux/sigaltstack_check"); - - pid_t child_pid = -1; - int execve_errno = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec(full_path, {"sigaltstack_check"}, {}, nullptr, &child_pid, - &execve_errno)); - - ASSERT_GT(child_pid, 0); - ASSERT_EQ(execve_errno, 0); - - int status = 0; - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), 0); -} - -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. - -void badhandler(int sig, siginfo_t* siginfo, void* arg) { - char stack_var = 0; - char* current_ss = &stack_var; - - stack_t stack; - int ret = sigaltstack(nullptr, &stack); - if (ret < 0 || (stack.ss_flags & SS_ONSTACK) != SS_ONSTACK) { - // We should always be marked as being on the stack. Don't allow this to hit - // the bottom if this is ever not true (the main test will fail as a - // result, but we still need to unwind the recursive faults). - badhandler_on_sigaltstack = false; - } - if (current_ss < badhandler_low_water_mark) { - // Record the low point for the signal stack. We never expected this to be - // before stack bottom, but this is asserted in the actual test. - badhandler_low_water_mark = current_ss; - } - if (badhandler_recursive_faults > 0) { - badhandler_recursive_faults--; - Fault(); - } - FixupFault(reinterpret_cast<ucontext_t*>(arg)); -} - -TEST(SigaltstackTest, WalksOffBottom) { - // This test marks the upper half of the stack_mem array as the signal stack. - // It asserts that when a fault occurs in the handler (already on the signal - // stack), we eventually continue to fault our way off the stack. We should - // not revert to the top of the signal stack when we fall off the bottom and - // the signal stack should remain "in use". When we fall off the signal stack, - // we should have an unconditional signal delivered and not start using the - // first part of the stack_mem array. - std::vector<char> stack_mem(SIGSTKSZ * 2); - stack_t stack = {}; - stack.ss_sp = stack_mem.data() + SIGSTKSZ; // See above: upper half. - stack.ss_size = SIGSTKSZ; // Only one half the array. - auto const cleanup_sigstack = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); - - // Setup the handler: this must be for SIGSEGV, and it must allow proper - // nesting (no signal mask, no defer) so that we can trigger multiple times. - // - // When we walk off the bottom of the signal stack and force signal delivery - // of a SIGSEGV, the handler will revert to the default behavior (kill). - struct sigaction sa = {}; - sa.sa_sigaction = badhandler; - sa.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_NODEFER; - auto const cleanup_sa = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); - - // Trigger a single fault. - badhandler_low_water_mark = - 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()); - EXPECT_EQ(stack.ss_flags & SS_ONSTACK, 0); - EXPECT_LT(badhandler_low_water_mark, - reinterpret_cast<char*>(stack.ss_sp) + 2 * SIGSTKSZ); - EXPECT_GT(badhandler_low_water_mark, reinterpret_cast<char*>(stack.ss_sp)); - - // Trigger two faults. - char* prev_low_water_mark = badhandler_low_water_mark; // Previous top. - badhandler_recursive_faults = 1; // One refault. - Fault(); - ASSERT_TRUE(badhandler_on_sigaltstack); - EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds()); - EXPECT_EQ(stack.ss_flags & SS_ONSTACK, 0); - EXPECT_LT(badhandler_low_water_mark, prev_low_water_mark); - EXPECT_GT(badhandler_low_water_mark, reinterpret_cast<char*>(stack.ss_sp)); - - // Calculate the stack growth for a fault, and set the recursive faults to - // ensure that the signal handler stack required exceeds our marked stack area - // by a minimal amount. It should remain in the valid stack_mem area so that - // we can test the signal is forced merely by going out of the signal stack - // bounds, not by a genuine fault. - uintptr_t frame_size = - static_cast<uintptr_t>(prev_low_water_mark - badhandler_low_water_mark); - badhandler_recursive_faults = (SIGSTKSZ + frame_size) / frame_size; - EXPECT_EXIT(Fault(), ::testing::KilledBySignal(SIGSEGV), ""); -} - -volatile int setonstack_retval = 0; // Set by the handler. -volatile int setonstack_errno = 0; // Set by the handler. - -void setonstack(int sig, siginfo_t* siginfo, void* arg) { - char stack_mem[SIGSTKSZ]; - stack_t stack = {}; - stack.ss_sp = &stack_mem[0]; - stack.ss_size = SIGSTKSZ; - setonstack_retval = sigaltstack(&stack, nullptr); - setonstack_errno = errno; - FixupFault(reinterpret_cast<ucontext_t*>(arg)); -} - -TEST(SigaltstackTest, SetWhileOnStack) { - // Reserve twice as much stack here, since the handler will allocate a vector - // of size SIGTKSZ and attempt to set the sigaltstack to that value. - std::vector<char> stack_mem(2 * SIGSTKSZ); - stack_t stack = {}; - stack.ss_sp = stack_mem.data(); - stack.ss_size = stack_mem.size(); - auto const cleanup_sigstack = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaltstack(stack)); - - // See above. - struct sigaction sa = {}; - sa.sa_sigaction = setonstack; - sa.sa_flags = SA_SIGINFO | SA_ONSTACK; - auto const cleanup_sa = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGSEGV, sa)); - - // Trigger a fault. - Fault(); - - // The set should have failed. - EXPECT_EQ(setonstack_retval, -1); - EXPECT_EQ(setonstack_errno, EPERM); -} - -TEST(SigaltstackTest, SetCurrentStack) { - // This is executed as an exit test because once the signal stack is set to - // the local stack, there's no good way to unwind. We don't want to taint the - // test of any other tests that might run within this process. - EXPECT_EXIT( - { - char stack_value = 0; - stack_t stack = {}; - stack.ss_sp = &stack_value - kPageSize; // Lower than current level. - stack.ss_size = 2 * kPageSize; // => &stack_value +/- kPageSize. - TEST_CHECK(sigaltstack(&stack, nullptr) == 0); - TEST_CHECK(sigaltstack(nullptr, &stack) == 0); - TEST_CHECK((stack.ss_flags & SS_ONSTACK) != 0); - - // Should not be able to change the stack (even no-op). - TEST_CHECK(sigaltstack(&stack, nullptr) == -1 && errno == EPERM); - - // Should not be able to disable the stack. - stack.ss_flags = SS_DISABLE; - TEST_CHECK(sigaltstack(&stack, nullptr) == -1 && errno == EPERM); - exit(0); - }, - ::testing::ExitedWithCode(0), ""); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sigaltstack_check.cc b/test/syscalls/linux/sigaltstack_check.cc deleted file mode 100644 index 5ac1b661d..000000000 --- a/test/syscalls/linux/sigaltstack_check.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Checks that there is no alternate signal stack by default. -// -// Used by a test in sigaltstack.cc. -#include <errno.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include "test/util/logging.h" - -int main(int /* argc */, char** /* argv */) { - stack_t stack; - TEST_CHECK(sigaltstack(nullptr, &stack) >= 0); - TEST_CHECK(stack.ss_flags == SS_DISABLE); - TEST_CHECK(stack.ss_sp == 0); - TEST_CHECK(stack.ss_size == 0); - return 0; -} diff --git a/test/syscalls/linux/signalfd.cc b/test/syscalls/linux/signalfd.cc deleted file mode 100644 index c86cd2755..000000000 --- a/test/syscalls/linux/signalfd.cc +++ /dev/null @@ -1,373 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <poll.h> -#include <signal.h> -#include <stdio.h> -#include <string.h> -#include <sys/signalfd.h> -#include <unistd.h> - -#include <functional> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/synchronization/mutex.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -using ::testing::KilledBySignal; - -namespace gvisor { -namespace testing { - -namespace { - -constexpr int kSigno = SIGUSR1; -constexpr int kSignoMax = 64; // SIGRTMAX -constexpr int kSignoAlt = SIGUSR2; - -// Returns a new signalfd. -inline PosixErrorOr<FileDescriptor> NewSignalFD(sigset_t* mask, int flags = 0) { - int fd = signalfd(-1, mask, flags); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "signalfd"); - } - return FileDescriptor(fd); -} - -class SignalfdTest : public ::testing::TestWithParam<int> {}; - -TEST_P(SignalfdTest, Basic) { - int signo = GetParam(); - // Create the signalfd. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, signo); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, 0)); - - // Deliver the blocked signal. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, signo)); - ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds()); - - // We should now read the signal. - struct signalfd_siginfo rbuf; - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(rbuf.ssi_signo, signo); -} - -TEST_P(SignalfdTest, MaskWorks) { - int signo = GetParam(); - // Create two signalfds with different masks. - sigset_t mask1, mask2; - sigemptyset(&mask1); - sigemptyset(&mask2); - sigaddset(&mask1, signo); - sigaddset(&mask2, kSignoAlt); - FileDescriptor fd1 = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask1, 0)); - FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask2, 0)); - - // Deliver the two signals. - const auto scoped_sigmask1 = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, signo)); - const auto scoped_sigmask2 = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, kSignoAlt)); - ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds()); - ASSERT_THAT(tgkill(getpid(), gettid(), kSignoAlt), SyscallSucceeds()); - - // We should see the signals on the appropriate signalfds. - // - // We read in the opposite order as the signals deliver above, to ensure that - // we don't happen to read the correct signal from the correct signalfd. - struct signalfd_siginfo rbuf1, rbuf2; - ASSERT_THAT(read(fd2.get(), &rbuf2, sizeof(rbuf2)), - SyscallSucceedsWithValue(sizeof(rbuf2))); - EXPECT_EQ(rbuf2.ssi_signo, kSignoAlt); - ASSERT_THAT(read(fd1.get(), &rbuf1, sizeof(rbuf1)), - SyscallSucceedsWithValue(sizeof(rbuf1))); - EXPECT_EQ(rbuf1.ssi_signo, signo); -} - -TEST(Signalfd, Cloexec) { - // Exec tests confirm that O_CLOEXEC has the intended effect. We just create a - // signalfd with the appropriate flag here and assert that the FD has it set. - sigset_t mask; - sigemptyset(&mask); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, SFD_CLOEXEC)); - EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST_P(SignalfdTest, Blocking) { - int signo = GetParam(); - // Create the signalfd in blocking mode. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, signo); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, 0)); - - // Shared tid variable. - absl::Mutex mu; - bool has_tid = false; - pid_t tid; - - // Start a thread reading. - ScopedThread t([&] { - // Copy the tid and notify the caller. - { - absl::MutexLock ml(&mu); - tid = gettid(); - has_tid = true; - } - - // Read the signal from the signalfd. - struct signalfd_siginfo rbuf; - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(rbuf.ssi_signo, signo); - }); - - // Wait until blocked. - absl::MutexLock ml(&mu); - mu.Await(absl::Condition(&has_tid)); - - // Deliver the signal to either the waiting thread, or - // to this thread. N.B. this is a bug in the core gVisor - // behavior for signalfd, and needs to be fixed. - // - // See gvisor.dev/issue/139. - if (IsRunningOnGvisor()) { - ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds()); - } else { - ASSERT_THAT(tgkill(getpid(), tid, signo), SyscallSucceeds()); - } - - // Ensure that it was received. - t.Join(); -} - -TEST_P(SignalfdTest, ThreadGroup) { - int signo = GetParam(); - // Create the signalfd in blocking mode. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, signo); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, 0)); - - // Shared variable. - absl::Mutex mu; - bool first = false; - bool second = false; - - // Start a thread reading. - ScopedThread t([&] { - // Read the signal from the signalfd. - struct signalfd_siginfo rbuf; - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(rbuf.ssi_signo, signo); - - // Wait for the other thread. - absl::MutexLock ml(&mu); - first = true; - mu.Await(absl::Condition(&second)); - }); - - // Deliver the signal to the threadgroup. - ASSERT_THAT(kill(getpid(), signo), SyscallSucceeds()); - - // Wait for the first thread to process. - { - absl::MutexLock ml(&mu); - mu.Await(absl::Condition(&first)); - } - - // Deliver to the thread group again (other thread still exists). - ASSERT_THAT(kill(getpid(), signo), SyscallSucceeds()); - - // Ensure that we can also receive it. - struct signalfd_siginfo rbuf; - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(rbuf.ssi_signo, signo); - - // Mark the test as done. - { - absl::MutexLock ml(&mu); - second = true; - } - - // The other thread should be joinable. - t.Join(); -} - -TEST_P(SignalfdTest, Nonblock) { - int signo = GetParam(); - // Create the signalfd in non-blocking mode. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, signo); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, SFD_NONBLOCK)); - - // We should return if we attempt to read. - struct signalfd_siginfo rbuf; - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Block and deliver the signal. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, signo)); - ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds()); - - // Ensure that a read actually works. - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(rbuf.ssi_signo, signo); - - // Should block again. - EXPECT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST_P(SignalfdTest, SetMask) { - int signo = GetParam(); - // Create the signalfd matching nothing. - sigset_t mask; - sigemptyset(&mask); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, SFD_NONBLOCK)); - - // Block and deliver a signal. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, signo)); - ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds()); - - // We should have nothing. - struct signalfd_siginfo rbuf; - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Change the signal mask. - sigaddset(&mask, signo); - ASSERT_THAT(signalfd(fd.get(), &mask, 0), SyscallSucceeds()); - - // We should now have the signal. - ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); - EXPECT_EQ(rbuf.ssi_signo, signo); -} - -TEST_P(SignalfdTest, Poll) { - int signo = GetParam(); - // Create the signalfd. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, signo); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, 0)); - - // Block the signal, and start a thread to deliver it. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, signo)); - pid_t orig_tid = gettid(); - ScopedThread t([&] { - absl::SleepFor(absl::Seconds(5)); - ASSERT_THAT(tgkill(getpid(), orig_tid, signo), SyscallSucceeds()); - }); - - // Start polling for the signal. We expect that it is not available at the - // outset, but then becomes available when the signal is sent. We give a - // timeout of 10000ms (or the delay above + 5 seconds of additional grace - // time). - struct pollfd poll_fd = {fd.get(), POLLIN, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), - SyscallSucceedsWithValue(1)); - - // Actually read the signal to prevent delivery. - struct signalfd_siginfo rbuf; - EXPECT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)), - SyscallSucceedsWithValue(sizeof(rbuf))); -} - -std::string PrintSigno(::testing::TestParamInfo<int> info) { - switch (info.param) { - case kSigno: - return "kSigno"; - case kSignoMax: - return "kSignoMax"; - default: - return absl::StrCat(info.param); - } -} -INSTANTIATE_TEST_SUITE_P(Signalfd, SignalfdTest, - ::testing::Values(kSigno, kSignoMax), PrintSigno); - -TEST(Signalfd, Ppoll) { - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGKILL); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, SFD_CLOEXEC)); - - // Ensure that the given ppoll blocks. - struct pollfd pfd = {}; - pfd.fd = fd.get(); - pfd.events = POLLIN; - struct timespec timeout = {}; - timeout.tv_sec = 1; - EXPECT_THAT(RetryEINTR(ppoll)(&pfd, 1, &timeout, &mask), - SyscallSucceedsWithValue(0)); -} - -TEST(Signalfd, KillStillKills) { - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGKILL); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, SFD_CLOEXEC)); - - // Just because there is a signalfd, we shouldn't see any change in behavior - // for unblockable signals. It's easier to test this with SIGKILL. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, SIGKILL)); - EXPECT_EXIT(tgkill(getpid(), gettid(), SIGKILL), KilledBySignal(SIGKILL), ""); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - // These tests depend on delivering signals. Block them up front so that all - // other threads created by TestInit will also have them blocked, and they - // will not interface with the rest of the test. - sigset_t set; - sigemptyset(&set); - sigaddset(&set, gvisor::testing::kSigno); - sigaddset(&set, gvisor::testing::kSignoMax); - sigaddset(&set, gvisor::testing::kSignoAlt); - TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); - - gvisor::testing::TestInit(&argc, &argv); - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/sigprocmask.cc b/test/syscalls/linux/sigprocmask.cc deleted file mode 100644 index a603fc1d1..000000000 --- a/test/syscalls/linux/sigprocmask.cc +++ /dev/null @@ -1,269 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <stddef.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Signals numbers used for testing. -static constexpr int kTestSignal1 = SIGUSR1; -static constexpr int kTestSignal2 = SIGUSR2; - -static int raw_sigprocmask(int how, const sigset_t* set, sigset_t* oldset) { - return syscall(SYS_rt_sigprocmask, how, set, oldset, _NSIG / 8); -} - -// count of the number of signals received -int signal_count[kMaxSignal + 1]; - -// signal handler increments the signal counter -void SigHandler(int sig, siginfo_t* info, void* context) { - TEST_CHECK(sig > 0 && sig <= kMaxSignal); - signal_count[sig] += 1; -} - -// The test fixture saves and restores the signal mask and -// sets up handlers for kTestSignal1 and kTestSignal2. -class SigProcMaskTest : public ::testing::Test { - protected: - void SetUp() override { - // Save the current signal mask. - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &mask_), - SyscallSucceeds()); - - // Setup signal handlers for kTestSignal1 and kTestSignal2. - struct sigaction sa; - sa.sa_sigaction = SigHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - EXPECT_THAT(sigaction(kTestSignal1, &sa, &sa_test_sig_1_), - SyscallSucceeds()); - EXPECT_THAT(sigaction(kTestSignal2, &sa, &sa_test_sig_2_), - SyscallSucceeds()); - - // Clear the signal counters. - memset(signal_count, 0, sizeof(signal_count)); - } - - void TearDown() override { - // Restore the signal mask. - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &mask_, nullptr), - SyscallSucceeds()); - - // Restore the signal handlers for kTestSignal1 and kTestSignal2. - EXPECT_THAT(sigaction(kTestSignal1, &sa_test_sig_1_, nullptr), - SyscallSucceeds()); - EXPECT_THAT(sigaction(kTestSignal2, &sa_test_sig_2_, nullptr), - SyscallSucceeds()); - } - - private: - sigset_t mask_; - struct sigaction sa_test_sig_1_; - struct sigaction sa_test_sig_2_; -}; - -// Both sigsets nullptr should succeed and do nothing. -TEST_F(SigProcMaskTest, NullAddress) { - EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, nullptr, NULL), SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, nullptr, NULL), SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, NULL), SyscallSucceeds()); -} - -// Bad address for either sigset should fail with EFAULT. -TEST_F(SigProcMaskTest, BadAddress) { - sigset_t* bad_addr = reinterpret_cast<sigset_t*>(-1); - - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, bad_addr, nullptr), - SyscallFailsWithErrno(EFAULT)); - - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, bad_addr), - SyscallFailsWithErrno(EFAULT)); -} - -// Bad value of the "how" parameter should fail with EINVAL. -TEST_F(SigProcMaskTest, BadParameter) { - int bad_param_1 = -1; - int bad_param_2 = 42; - - sigset_t set1; - sigemptyset(&set1); - - EXPECT_THAT(raw_sigprocmask(bad_param_1, &set1, nullptr), - SyscallFailsWithErrno(EINVAL)); - - EXPECT_THAT(raw_sigprocmask(bad_param_2, &set1, nullptr), - SyscallFailsWithErrno(EINVAL)); -} - -// Check that we can get the current signal mask. -TEST_F(SigProcMaskTest, GetMask) { - sigset_t set1; - sigset_t set2; - - sigemptyset(&set1); - sigfillset(&set2); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &set1), SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &set2), SyscallSucceeds()); - EXPECT_THAT(set1, EqualsSigset(set2)); -} - -// Check that we can set the signal mask. -TEST_F(SigProcMaskTest, SetMask) { - sigset_t actual; - sigset_t expected; - - // Try to mask all signals - sigfillset(&expected); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), - SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), - SyscallSucceeds()); - // sigprocmask() should have silently ignored SIGKILL and SIGSTOP. - sigdelset(&expected, SIGSTOP); - sigdelset(&expected, SIGKILL); - EXPECT_THAT(actual, EqualsSigset(expected)); - - // Try to clear the signal mask - sigemptyset(&expected); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), - SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), - SyscallSucceeds()); - EXPECT_THAT(actual, EqualsSigset(expected)); - - // Try to set a mask with one signal. - sigemptyset(&expected); - sigaddset(&expected, kTestSignal1); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), - SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), - SyscallSucceeds()); - EXPECT_THAT(actual, EqualsSigset(expected)); -} - -// Check that we can add and remove signals. -TEST_F(SigProcMaskTest, BlockUnblock) { - sigset_t actual; - sigset_t expected; - - // Try to set a mask with one signal. - sigemptyset(&expected); - sigaddset(&expected, kTestSignal1); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &expected, nullptr), - SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), - SyscallSucceeds()); - EXPECT_THAT(actual, EqualsSigset(expected)); - - // Try to add another signal. - sigset_t block; - sigemptyset(&block); - sigaddset(&block, kTestSignal2); - EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, &block, nullptr), SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), - SyscallSucceeds()); - sigaddset(&expected, kTestSignal2); - EXPECT_THAT(actual, EqualsSigset(expected)); - - // Try to remove a signal. - sigset_t unblock; - sigemptyset(&unblock); - sigaddset(&unblock, kTestSignal1); - EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, &unblock, nullptr), - SyscallSucceeds()); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, nullptr, &actual), - SyscallSucceeds()); - sigdelset(&expected, kTestSignal1); - EXPECT_THAT(actual, EqualsSigset(expected)); -} - -// Test that the signal mask actually blocks signals. -TEST_F(SigProcMaskTest, SignalHandler) { - sigset_t mask; - - // clear the signal mask - sigemptyset(&mask); - EXPECT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, nullptr), SyscallSucceeds()); - - // Check the initial signal counts. - EXPECT_EQ(0, signal_count[kTestSignal1]); - EXPECT_EQ(0, signal_count[kTestSignal2]); - - // Check that both kTestSignal1 and kTestSignal2 are not blocked. - raise(kTestSignal1); - raise(kTestSignal2); - EXPECT_EQ(1, signal_count[kTestSignal1]); - EXPECT_EQ(1, signal_count[kTestSignal2]); - - // Block kTestSignal1. - sigaddset(&mask, kTestSignal1); - EXPECT_THAT(raw_sigprocmask(SIG_BLOCK, &mask, nullptr), SyscallSucceeds()); - - // Check that kTestSignal1 is blocked. - raise(kTestSignal1); - raise(kTestSignal2); - EXPECT_EQ(1, signal_count[kTestSignal1]); - EXPECT_EQ(2, signal_count[kTestSignal2]); - - // Unblock kTestSignal1. - sigaddset(&mask, kTestSignal1); - EXPECT_THAT(raw_sigprocmask(SIG_UNBLOCK, &mask, nullptr), SyscallSucceeds()); - - // Check that the unblocked kTestSignal1 has been delivered. - EXPECT_EQ(2, signal_count[kTestSignal1]); - EXPECT_EQ(2, signal_count[kTestSignal2]); -} - -// Check that sigprocmask correctly handles aliasing of the set and oldset -// pointers. Regression test for b/30502311. -TEST_F(SigProcMaskTest, AliasedSets) { - sigset_t mask; - - // Set a mask in which only kTestSignal1 is blocked. - sigset_t mask1; - sigemptyset(&mask1); - sigaddset(&mask1, kTestSignal1); - mask = mask1; - ASSERT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, nullptr), SyscallSucceeds()); - - // Exchange it with a mask in which only kTestSignal2 is blocked. - sigset_t mask2; - sigemptyset(&mask2); - sigaddset(&mask2, kTestSignal2); - mask = mask2; - ASSERT_THAT(raw_sigprocmask(SIG_SETMASK, &mask, &mask), SyscallSucceeds()); - - // Check that the exchange succeeeded: - // mask should now contain the previously-set mask blocking only kTestSignal1. - EXPECT_THAT(mask, EqualsSigset(mask1)); - // The current mask should block only kTestSignal2. - ASSERT_THAT(raw_sigprocmask(0, nullptr, &mask), SyscallSucceeds()); - EXPECT_THAT(mask, EqualsSigset(mask2)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sigreturn_amd64.cc b/test/syscalls/linux/sigreturn_amd64.cc deleted file mode 100644 index 6227774a4..000000000 --- a/test/syscalls/linux/sigreturn_amd64.cc +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/types.h> -#include <sys/ucontext.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/logging.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr uint64_t kOrigRcx = 0xdeadbeeffacefeed; -constexpr uint64_t kOrigR11 = 0xfacefeedbaad1dea; - -volatile int gotvtalrm, ready; - -void sigvtalrm(int sig, siginfo_t* siginfo, void* _uc) { - ucontext_t* uc = reinterpret_cast<ucontext_t*>(_uc); - - // Verify that: - // - test is in the busy-wait loop waiting for signal. - // - %rcx and %r11 values in mcontext_t match kOrigRcx and kOrigR11. - if (ready && - static_cast<uint64_t>(uc->uc_mcontext.gregs[REG_RCX]) == kOrigRcx && - static_cast<uint64_t>(uc->uc_mcontext.gregs[REG_R11]) == kOrigR11) { - // Modify the values %rcx and %r11 in the ucontext. These are the - // values seen by the application after the signal handler returns. - uc->uc_mcontext.gregs[REG_RCX] = ~kOrigRcx; - uc->uc_mcontext.gregs[REG_R11] = ~kOrigR11; - gotvtalrm = 1; - } -} - -TEST(SigIretTest, CheckRcxR11) { - // Setup signal handler for SIGVTALRM. - struct sigaction sa = {}; - sigfillset(&sa.sa_mask); - sa.sa_sigaction = sigvtalrm; - sa.sa_flags = SA_SIGINFO; - auto const action_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGVTALRM, sa)); - - auto const mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGVTALRM)); - - // Setup itimer to fire after 500 msecs. - struct itimerval itimer = {}; - itimer.it_value.tv_usec = 500 * 1000; // 500 msecs. - auto const timer_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_VIRTUAL, itimer)); - - // Initialize %rcx and %r11 and spin until the signal handler returns. - uint64_t rcx = kOrigRcx; - uint64_t r11 = kOrigR11; - asm volatile( - "movq %[rcx], %%rcx;" // %rcx = rcx - "movq %[r11], %%r11;" // %r11 = r11 - "movl $1, %[ready];" // ready = 1 - "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) - : "cc", "memory", "rcx", "r11"); - - // If sigreturn(2) returns via 'sysret' then %rcx and %r11 will be - // clobbered and set to 'ptregs->rip' and 'ptregs->rflags' respectively. - // - // The following check verifies that %rcx and %r11 were not clobbered - // when returning from the signal handler (via sigreturn(2)). - EXPECT_EQ(rcx, ~kOrigRcx); - EXPECT_EQ(r11, ~kOrigR11); -} - -constexpr uint64_t kNonCanonicalRip = 0xCCCC000000000000; - -// Test that a non-canonical signal handler faults as expected. -TEST(SigIretTest, BadHandler) { - struct sigaction sa = {}; - sa.sa_sigaction = - reinterpret_cast<void (*)(int, siginfo_t*, void*)>(kNonCanonicalRip); - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); - - pid_t pid = fork(); - if (pid == 0) { - // Child, wait for signal. - while (1) { - pause(); - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - - EXPECT_THAT(kill(pid, SIGUSR1), SyscallSucceeds()); - - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) - << "status = " << status; -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - // SigIretTest.CheckRcxR11 depends on delivering SIGVTALRM to the main thread. - // Block SIGVTALRM so that any other threads created by TestInit will also - // have SIGVTALRM blocked. - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGVTALRM); - TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/sigreturn_arm64.cc b/test/syscalls/linux/sigreturn_arm64.cc deleted file mode 100644 index 2c19e2984..000000000 --- a/test/syscalls/linux/sigreturn_arm64.cc +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2021 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 <linux/unistd.h> -#include <signal.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/ucontext.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/logging.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr uint64_t kOrigX7 = 0xdeadbeeffacefeed; - -void sigvtalrm(int sig, siginfo_t* siginfo, void* _uc) { - ucontext_t* uc = reinterpret_cast<ucontext_t*>(_uc); - - // Verify that: - // - x7 value in mcontext_t matches kOrigX7. - if (uc->uc_mcontext.regs[7] == kOrigX7) { - // Modify the value x7 in the ucontext. This is the value seen by the - // application after the signal handler returns. - uc->uc_mcontext.regs[7] = ~kOrigX7; - } -} - -int testX7(uint64_t* val, uint64_t sysno, uint64_t tgid, uint64_t tid, - uint64_t signo) { - register uint64_t* x9 __asm__("x9") = val; - register uint64_t x8 __asm__("x8") = sysno; - register uint64_t x0 __asm__("x0") = tgid; - register uint64_t x1 __asm__("x1") = tid; - register uint64_t x2 __asm__("x2") = signo; - - // Initialize x7, send SIGVTALRM to itself and read x7. - __asm__( - "ldr x7, [x9, 0]\n" - "svc 0\n" - "str x7, [x9, 0]\n" - : "=r"(x0) - : "r"(x0), "r"(x1), "r"(x2), "r"(x9), "r"(x8) - : "x7"); - return x0; -} - -// On ARM64, when ptrace stops on a system call, it uses the x7 register to -// indicate whether the stop has been signalled from syscall entry or syscall -// exit. This means that we can't get a value of this register and we can't -// change it. More details are in the comment for tracehook_report_syscall in -// arch/arm64/kernel/ptrace.c. -// -// CheckR7 checks that the ptrace platform handles the x7 register properly. -TEST(SigreturnTest, CheckX7) { - // Setup signal handler for SIGVTALRM. - struct sigaction sa = {}; - sigfillset(&sa.sa_mask); - sa.sa_sigaction = sigvtalrm; - sa.sa_flags = SA_SIGINFO; - auto const action_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGVTALRM, sa)); - - auto const mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGVTALRM)); - - uint64_t x7 = kOrigX7; - - testX7(&x7, __NR_tgkill, getpid(), syscall(__NR_gettid), SIGVTALRM); - - // The following check verifies that %x7 was not clobbered - // when returning from the signal handler (via sigreturn(2)). - EXPECT_EQ(x7, ~kOrigX7); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sigstop.cc b/test/syscalls/linux/sigstop.cc deleted file mode 100644 index b2fcedd62..000000000 --- a/test/syscalls/linux/sigstop.cc +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <stdlib.h> -#include <sys/select.h> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(bool, sigstop_test_child, false, - "If true, run the SigstopTest child workload."); - -namespace gvisor { -namespace testing { - -namespace { - -constexpr absl::Duration kChildStartupDelay = absl::Seconds(5); -constexpr absl::Duration kChildMainThreadDelay = absl::Seconds(10); -constexpr absl::Duration kChildExtraThreadDelay = absl::Seconds(15); -constexpr absl::Duration kPostSIGSTOPDelay = absl::Seconds(20); - -// Comparisons on absl::Duration aren't yet constexpr (2017-07-14), so we -// can't just use static_assert. -TEST(SigstopTest, TimesAreRelativelyConsistent) { - EXPECT_LT(kChildStartupDelay, kChildMainThreadDelay) - << "Child process will exit before the parent process attempts to stop " - "it"; - EXPECT_LT(kChildMainThreadDelay, kChildExtraThreadDelay) - << "Secondary thread in child process will exit before main thread, " - "causing it to exit with the wrong code"; - EXPECT_LT(kChildExtraThreadDelay, kPostSIGSTOPDelay) - << "Parent process stops waiting before child process may exit if " - "improperly stopped, rendering the test ineffective"; -} - -// Exit codes communicated from the child workload to the parent test process. -constexpr int kChildMainThreadExitCode = 10; -constexpr int kChildExtraThreadExitCode = 11; - -TEST(SigstopTest, Correctness) { - pid_t child_pid = -1; - int execve_errno = 0; - auto cleanup = ASSERT_NO_ERRNO_AND_VALUE( - ForkAndExec("/proc/self/exe", {"/proc/self/exe", "--sigstop_test_child"}, - {}, nullptr, &child_pid, &execve_errno)); - - ASSERT_GT(child_pid, 0); - ASSERT_EQ(execve_errno, 0); - - // Wait for the child subprocess to start the second thread before stopping - // it. - absl::SleepFor(kChildStartupDelay); - ASSERT_THAT(kill(child_pid, SIGSTOP), SyscallSucceeds()); - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, WUNTRACED), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFSTOPPED(status)); - EXPECT_EQ(SIGSTOP, WSTOPSIG(status)); - - // Sleep for longer than either of the sleeps in the child subprocess, - // expecting the child to stay alive because it's stopped. - absl::SleepFor(kPostSIGSTOPDelay); - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, WNOHANG), - SyscallSucceedsWithValue(0)); - - // Resume the child. - ASSERT_THAT(kill(child_pid, SIGCONT), SyscallSucceeds()); - - EXPECT_THAT(RetryEINTR(waitpid)(child_pid, &status, WCONTINUED), - SyscallSucceedsWithValue(child_pid)); - EXPECT_TRUE(WIFCONTINUED(status)); - - // Expect it to die. - ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0), SyscallSucceeds()); - ASSERT_TRUE(WIFEXITED(status)); - ASSERT_EQ(WEXITSTATUS(status), kChildMainThreadExitCode); -} - -// Like base:SleepFor, but tries to avoid counting time spent stopped due to a -// stop signal toward the sleep. -// -// This is required due to an inconsistency in how nanosleep(2) and stop signals -// interact on Linux. When nanosleep is interrupted, it writes the remaining -// time back to its second timespec argument, so that if nanosleep is -// interrupted by a signal handler then userspace can immediately call nanosleep -// again with that timespec. However, if nanosleep is automatically restarted -// (because it's interrupted by a signal that is not delivered to a handler, -// such as a stop signal), it's restarted based on the timer's former *absolute* -// expiration time (via ERESTART_RESTARTBLOCK => SYS_restart_syscall => -// hrtimer_nanosleep_restart). This means that time spent stopped is effectively -// counted as time spent sleeping, resulting in less time spent sleeping than -// expected. -// -// Dividing the sleep into multiple smaller sleeps limits the impact of this -// effect to the length of each sleep during which a stop occurs; for example, -// if a sleeping process is only stopped once, SleepIgnoreStopped can -// under-sleep by at most 100ms. -void SleepIgnoreStopped(absl::Duration d) { - absl::Duration const max_sleep = absl::Milliseconds(100); - while (d > absl::ZeroDuration()) { - absl::Duration to_sleep = std::min(d, max_sleep); - absl::SleepFor(to_sleep); - d -= to_sleep; - } -} - -void RunChild() { - // Start another thread that attempts to call exit_group with a different - // error code, in order to verify that SIGSTOP stops this thread as well. - ScopedThread t([] { - SleepIgnoreStopped(kChildExtraThreadDelay); - exit(kChildExtraThreadExitCode); - }); - SleepIgnoreStopped(kChildMainThreadDelay); - exit(kChildMainThreadExitCode); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_sigstop_test_child)) { - gvisor::testing::RunChild(); - return 1; - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/sigtimedwait.cc b/test/syscalls/linux/sigtimedwait.cc deleted file mode 100644 index 4f8afff15..000000000 --- a/test/syscalls/linux/sigtimedwait.cc +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/wait.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" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// N.B. main() blocks SIGALRM and SIGCHLD on all threads. - -constexpr int kAlarmSecs = 12; - -void NoopHandler(int sig, siginfo_t* info, void* context) {} - -TEST(SigtimedwaitTest, InvalidTimeout) { - sigset_t mask; - sigemptyset(&mask); - struct timespec timeout = {0, 1000000001}; - EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), - SyscallFailsWithErrno(EINVAL)); - timeout = {-1, 0}; - EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), - SyscallFailsWithErrno(EINVAL)); - timeout = {0, -1}; - EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), - SyscallFailsWithErrno(EINVAL)); -} - -// No random save as the test relies on alarm timing. Cooperative save tests -// already cover the save between alarm and wait. -TEST(SigtimedwaitTest, AlarmReturnsAlarm_NoRandomSave) { - struct itimerval itv = {}; - itv.it_value.tv_sec = kAlarmSecs; - const auto itimer_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv)); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGALRM); - siginfo_t info = {}; - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, &info, nullptr), - SyscallSucceedsWithValue(SIGALRM)); - EXPECT_EQ(SIGALRM, info.si_signo); -} - -// No random save as the test relies on alarm timing. Cooperative save tests -// already cover the save between alarm and wait. -TEST(SigtimedwaitTest, NullTimeoutReturnsEINTR_NoRandomSave) { - struct sigaction sa; - sa.sa_sigaction = NoopHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - const auto action_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGALRM, sa)); - - const auto mask_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, SIGALRM)); - - struct itimerval itv = {}; - itv.it_value.tv_sec = kAlarmSecs; - const auto itimer_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_REAL, itv)); - - sigset_t mask; - sigemptyset(&mask); - EXPECT_THAT(sigtimedwait(&mask, nullptr, nullptr), - SyscallFailsWithErrno(EINTR)); -} - -TEST(SigtimedwaitTest, LegitTimeoutReturnsEAGAIN) { - sigset_t mask; - sigemptyset(&mask); - struct timespec timeout = {1, 0}; // 1 second - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &timeout), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(SigtimedwaitTest, ZeroTimeoutReturnsEAGAIN) { - sigset_t mask; - sigemptyset(&mask); - struct timespec timeout = {0, 0}; // 0 second - EXPECT_THAT(sigtimedwait(&mask, nullptr, &timeout), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(SigtimedwaitTest, KillGeneratedSIGCHLD) { - EXPECT_THAT(kill(getpid(), SIGCHLD), SyscallSucceeds()); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - struct timespec ts = {5, 0}; - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &ts), - SyscallSucceedsWithValue(SIGCHLD)); -} - -TEST(SigtimedwaitTest, ChildExitGeneratedSIGCHLD) { - pid_t pid = fork(); - if (pid == 0) { - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - struct timespec ts = {5, 0}; - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &ts), - SyscallSucceedsWithValue(SIGCHLD)); -} - -TEST(SigtimedwaitTest, ChildExitGeneratedSIGCHLDWithHandler) { - // Setup handler for SIGCHLD, but don't unblock it. - struct sigaction sa; - sa.sa_sigaction = NoopHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - const auto action_cleanup = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); - - pid_t pid = fork(); - if (pid == 0) { - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGCHLD); - struct timespec ts = {5, 0}; - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &ts), - SyscallSucceedsWithValue(SIGCHLD)); - - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; -} - -// sigtimedwait cannot catch SIGKILL. -TEST(SigtimedwaitTest, SIGKILLUncaught) { - // This is a regression test for sigtimedwait dequeuing SIGKILLs, thus - // preventing the task from exiting. - // - // The explanation below is specific to behavior in gVisor. The Linux behavior - // here is irrelevant because without a bug that prevents delivery of SIGKILL, - // none of this behavior is visible (in Linux or gVisor). - // - // SIGKILL is rather intrusive. Simply sending the SIGKILL marks - // ThreadGroup.exitStatus as exiting with SIGKILL, before the SIGKILL is even - // delivered. - // - // As a result, we cannot simply exit the child with a different exit code if - // it survives and expect to see that code in waitpid because: - // 1. PrepareGroupExit will override Task.exitStatus with - // ThreadGroup.exitStatus. - // 2. waitpid(2) will always return ThreadGroup.exitStatus rather than - // Task.exitStatus. - // - // We could use exit(2) to set Task.exitStatus without override, and a SIGCHLD - // handler to receive Task.exitStatus in the parent, but with that much - // test complexity, it is cleaner to simply use a pipe to notify the parent - // that we survived. - constexpr auto kSigtimedwaitSetupTime = absl::Seconds(2); - - int pipe_fds[2]; - ASSERT_THAT(pipe(pipe_fds), SyscallSucceeds()); - FileDescriptor rfd(pipe_fds[0]); - FileDescriptor wfd(pipe_fds[1]); - - pid_t pid = fork(); - if (pid == 0) { - rfd.reset(); - - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, SIGKILL); - RetryEINTR(sigtimedwait)(&mask, nullptr, nullptr); - - // Survived. - char c = 'a'; - TEST_PCHECK(WriteFd(wfd.get(), &c, 1) == 1); - _exit(1); - } - ASSERT_THAT(pid, SyscallSucceeds()); - - wfd.reset(); - - // Wait for child to block in sigtimedwait, then kill it. - absl::SleepFor(kSigtimedwaitSetupTime); - - // Sending SIGKILL will attempt to enqueue the signal twice: once in the - // normal signal sending path, and once to all Tasks in the ThreadGroup when - // applying SIGKILL side-effects. - // - // If we use kill(2), the former will be on the ThreadGroup signal queue and - // the latter will be on the Task signal queue. sigtimedwait can only dequeue - // one signal, so the other would kill the Task, masking bugs. - // - // If we use tkill(2), the former will be on the Task signal queue and the - // latter will be dropped as a duplicate. Then sigtimedwait can theoretically - // dequeue the single SIGKILL. - EXPECT_THAT(syscall(SYS_tkill, pid, SIGKILL), SyscallSucceeds()); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(pid, &status, 0), - SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) << status; - - // Child shouldn't have survived. - char c; - EXPECT_THAT(ReadFd(rfd.get(), &c, 1), SyscallSucceedsWithValue(0)); -} - -TEST(SigtimedwaitTest, IgnoredUnmaskedSignal) { - constexpr int kSigno = SIGUSR1; - constexpr auto kSigtimedwaitSetupTime = absl::Seconds(2); - constexpr auto kSigtimedwaitTimeout = absl::Seconds(5); - ASSERT_GT(kSigtimedwaitTimeout, kSigtimedwaitSetupTime); - - // Ensure that kSigno is ignored, and unmasked on this thread. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - const auto scoped_sigaction = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, mask)); - - // Create a thread which will send us kSigno while we are blocked in - // sigtimedwait. - pid_t tid = gettid(); - ScopedThread sigthread([&] { - absl::SleepFor(kSigtimedwaitSetupTime); - EXPECT_THAT(tgkill(getpid(), tid, kSigno), SyscallSucceeds()); - }); - - // sigtimedwait should not observe kSigno since it is ignored and already - // unmasked, causing it to be dropped before it is enqueued. - struct timespec timeout_ts = absl::ToTimespec(kSigtimedwaitTimeout); - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &timeout_ts), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(SigtimedwaitTest, IgnoredMaskedSignal) { - constexpr int kSigno = SIGUSR1; - constexpr auto kSigtimedwaitSetupTime = absl::Seconds(2); - constexpr auto kSigtimedwaitTimeout = absl::Seconds(5); - ASSERT_GT(kSigtimedwaitTimeout, kSigtimedwaitSetupTime); - - // Ensure that kSigno is ignored, and masked on this thread. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - const auto scoped_sigaction = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - // Create a thread which will send us kSigno while we are blocked in - // sigtimedwait. - pid_t tid = gettid(); - ScopedThread sigthread([&] { - absl::SleepFor(kSigtimedwaitSetupTime); - EXPECT_THAT(tgkill(getpid(), tid, kSigno), SyscallSucceeds()); - }); - - // sigtimedwait should observe kSigno since it is normally masked, causing it - // to be enqueued despite being ignored. - struct timespec timeout_ts = absl::ToTimespec(kSigtimedwaitTimeout); - EXPECT_THAT(RetryEINTR(sigtimedwait)(&mask, nullptr, &timeout_ts), - SyscallSucceedsWithValue(kSigno)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - // These tests depend on delivering SIGALRM/SIGCHLD to the main thread or in - // sigtimedwait. Block them so that any other threads created by TestInit will - // also have them blocked. - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGALRM); - sigaddset(&set, SIGCHLD); - TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); - - gvisor::testing::TestInit(&argc, &argv); - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/socket.cc b/test/syscalls/linux/socket.cc deleted file mode 100644 index b616c2c87..000000000 --- a/test/syscalls/linux/socket.cc +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_umask.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// From linux/magic.h, but we can't depend on linux headers here. -#define SOCKFS_MAGIC 0x534F434B - -TEST(SocketTest, UnixSocketPairProtocol) { - int socks[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, PF_UNIX, socks), - SyscallSucceeds()); - close(socks[0]); - close(socks[1]); -} - -TEST(SocketTest, ProtocolUnix) { - struct { - int domain, type, protocol; - } tests[] = { - {AF_UNIX, SOCK_STREAM, PF_UNIX}, - {AF_UNIX, SOCK_SEQPACKET, PF_UNIX}, - {AF_UNIX, SOCK_DGRAM, PF_UNIX}, - }; - for (long unsigned int i = 0; i < ABSL_ARRAYSIZE(tests); i++) { - ASSERT_NO_ERRNO_AND_VALUE( - Socket(tests[i].domain, tests[i].type, tests[i].protocol)); - } -} - -TEST(SocketTest, ProtocolInet) { - struct { - int domain, type, protocol; - } tests[] = { - {AF_INET, SOCK_DGRAM, IPPROTO_UDP}, - {AF_INET, SOCK_STREAM, IPPROTO_TCP}, - }; - for (long unsigned int i = 0; i < ABSL_ARRAYSIZE(tests); i++) { - ASSERT_NO_ERRNO_AND_VALUE( - Socket(tests[i].domain, tests[i].type, tests[i].protocol)); - } -} - -TEST(SocketTest, UnixSocketStat) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor bound = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX)); - - // The permissions of the file created with bind(2) should be defined by the - // permissions of the bound socket and the umask. - mode_t sock_perm = 0765, mask = 0123; - ASSERT_THAT(fchmod(bound.get(), sock_perm), SyscallSucceeds()); - TempUmask m(mask); - - struct sockaddr_un addr = - ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(/*abstract=*/false, AF_UNIX)); - ASSERT_THAT(bind(bound.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); - - struct stat statbuf = {}; - ASSERT_THAT(stat(addr.sun_path, &statbuf), SyscallSucceeds()); - - // Mode should be S_IFSOCK. - EXPECT_EQ(statbuf.st_mode, S_IFSOCK | (sock_perm & ~mask)); - - // Timestamps should be equal and non-zero. - if (!IsRunningWithVFS1()) { - EXPECT_NE(statbuf.st_atime, 0); - EXPECT_EQ(statbuf.st_atime, statbuf.st_mtime); - EXPECT_EQ(statbuf.st_atime, statbuf.st_ctime); - } -} - -TEST(SocketTest, UnixSocketStatFS) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor bound = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX)); - - struct statfs st; - EXPECT_THAT(fstatfs(bound.get(), &st), SyscallSucceeds()); - EXPECT_EQ(st.f_type, SOCKFS_MAGIC); - EXPECT_EQ(st.f_bsize, getpagesize()); - EXPECT_EQ(st.f_namelen, NAME_MAX); -} - -TEST(SocketTest, UnixSCMRightsOnlyPassedOnce_NoRandomSave) { - const DisableSave ds; - - int sockets[2]; - ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); - // Send more than what will fit inside the send/receive buffers, so that it is - // split into multiple messages. - constexpr int kBufSize = 0x100000; - - pid_t pid = fork(); - if (pid == 0) { - TEST_PCHECK(close(sockets[0]) == 0); - - // Construct a message with some control message. - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int))] = {}; - std::vector<char> buf(kBufSize); - struct iovec iov = {}; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - ((int*)CMSG_DATA(cmsg))[0] = sockets[1]; - - iov.iov_base = buf.data(); - iov.iov_len = kBufSize; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - int n = sendmsg(sockets[1], &msg, 0); - TEST_PCHECK(n == kBufSize); - TEST_PCHECK(shutdown(sockets[1], SHUT_RDWR) == 0); - TEST_PCHECK(close(sockets[1]) == 0); - _exit(0); - } - - close(sockets[1]); - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int))] = {}; - std::vector<char> buf(kBufSize); - struct iovec iov = {}; - msg.msg_control = &control; - msg.msg_controllen = sizeof(control); - - iov.iov_base = buf.data(); - iov.iov_len = kBufSize; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - // The control message should only be present in the first message received. - int n; - ASSERT_THAT(n = recvmsg(sockets[0], &msg, 0), SyscallSucceeds()); - ASSERT_GT(n, 0); - ASSERT_EQ(msg.msg_controllen, CMSG_SPACE(sizeof(int))); - - while (n > 0) { - ASSERT_THAT(n = recvmsg(sockets[0], &msg, 0), SyscallSucceeds()); - ASSERT_EQ(msg.msg_controllen, 0); - } - - close(sockets[0]); - - int status; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); -} - -using SocketOpenTest = ::testing::TestWithParam<int>; - -// UDS cannot be opened. -TEST_P(SocketOpenTest, Unix) { - // FIXME(b/142001530): Open incorrectly succeeds on gVisor. - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor bound = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX)); - - struct sockaddr_un addr = - ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(/*abstract=*/false, AF_UNIX)); - - ASSERT_THAT(bind(bound.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); - - EXPECT_THAT(open(addr.sun_path, GetParam()), SyscallFailsWithErrno(ENXIO)); -} - -INSTANTIATE_TEST_SUITE_P(OpenModes, SocketOpenTest, - ::testing::Values(O_RDONLY, O_RDWR)); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_abstract.cc b/test/syscalls/linux/socket_abstract.cc deleted file mode 100644 index 00999f192..000000000 --- a/test/syscalls/linux/socket_abstract.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_generic.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/socket_unix.h" -#include "test/syscalls/linux/socket_unix_cmsg.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P( - AbstractUnixSockets, AllSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - AbstractUnixSockets, UnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - AbstractUnixSockets, UnixSocketPairCmsgTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_bind_to_device.cc b/test/syscalls/linux/socket_bind_to_device.cc deleted file mode 100644 index 6b27f6eab..000000000 --- a/test/syscalls/linux/socket_bind_to_device.cc +++ /dev/null @@ -1,313 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <linux/if_tun.h> -#include <net/if.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> -#include <cstring> -#include <map> -#include <memory> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_bind_to_device_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -using std::string; - -// Test fixture for SO_BINDTODEVICE tests. -class BindToDeviceTest : public ::testing::TestWithParam<SocketKind> { - protected: - void SetUp() override { - printf("Testing case: %s\n", GetParam().description.c_str()); - ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) - << "CAP_NET_RAW is required to use SO_BINDTODEVICE"; - - interface_name_ = "eth1"; - auto interface_names = GetInterfaceNames(); - if (interface_names.find(interface_name_) == interface_names.end()) { - // Need a tunnel. - tunnel_ = ASSERT_NO_ERRNO_AND_VALUE(Tunnel::New()); - interface_name_ = tunnel_->GetName(); - ASSERT_FALSE(interface_name_.empty()); - } - socket_ = ASSERT_NO_ERRNO_AND_VALUE(GetParam().Create()); - } - - string interface_name() const { return interface_name_; } - - int socket_fd() const { return socket_->get(); } - - private: - std::unique_ptr<Tunnel> tunnel_; - string interface_name_; - std::unique_ptr<FileDescriptor> socket_; -}; - -constexpr char kIllegalIfnameChar = '/'; - -// Tests getsockopt of the default value. -TEST_P(BindToDeviceTest, GetsockoptDefault) { - char name_buffer[IFNAMSIZ * 2]; - char original_name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - // Read the default SO_BINDTODEVICE. - memset(original_name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - for (size_t i = 0; i <= sizeof(name_buffer); i++) { - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = i; - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, &name_buffer_size), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(name_buffer_size, 0); - EXPECT_EQ(memcmp(name_buffer, original_name_buffer, sizeof(name_buffer)), - 0); - } -} - -// Tests setsockopt of invalid device name. -TEST_P(BindToDeviceTest, SetsockoptInvalidDeviceName) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - // Set an invalid device name. - memset(name_buffer, kIllegalIfnameChar, 5); - name_buffer_size = 5; - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - name_buffer_size), - SyscallFailsWithErrno(ENODEV)); -} - -// Tests setsockopt of a buffer with a valid device name but not -// null-terminated, with different sizes of buffer. -TEST_P(BindToDeviceTest, SetsockoptValidDeviceNameWithoutNullTermination) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - strncpy(name_buffer, interface_name().c_str(), interface_name().size() + 1); - // Intentionally overwrite the null at the end. - memset(name_buffer + interface_name().size(), kIllegalIfnameChar, - sizeof(name_buffer) - interface_name().size()); - for (size_t i = 1; i <= sizeof(name_buffer); i++) { - name_buffer_size = i; - SCOPED_TRACE(absl::StrCat("Buffer size: ", i)); - // It should only work if the size provided is exactly right. - if (name_buffer_size == interface_name().size()) { - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, name_buffer_size), - SyscallSucceeds()); - } else { - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, name_buffer_size), - SyscallFailsWithErrno(ENODEV)); - } - } -} - -// Tests setsockopt of a buffer with a valid device name and null-terminated, -// with different sizes of buffer. -TEST_P(BindToDeviceTest, SetsockoptValidDeviceNameWithNullTermination) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - strncpy(name_buffer, interface_name().c_str(), interface_name().size() + 1); - // Don't overwrite the null at the end. - memset(name_buffer + interface_name().size() + 1, kIllegalIfnameChar, - sizeof(name_buffer) - interface_name().size() - 1); - for (size_t i = 1; i <= sizeof(name_buffer); i++) { - name_buffer_size = i; - SCOPED_TRACE(absl::StrCat("Buffer size: ", i)); - // It should only work if the size provided is at least the right size. - if (name_buffer_size >= interface_name().size()) { - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, name_buffer_size), - SyscallSucceeds()); - } else { - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, name_buffer_size), - SyscallFailsWithErrno(ENODEV)); - } - } -} - -// Tests that setsockopt of an invalid device name doesn't unset the previous -// valid setsockopt. -TEST_P(BindToDeviceTest, SetsockoptValidThenInvalid) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - // Write successfully. - strncpy(name_buffer, interface_name().c_str(), sizeof(name_buffer)); - ASSERT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - sizeof(name_buffer)), - SyscallSucceeds()); - - // Read it back successfully. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, interface_name().size() + 1); - EXPECT_STREQ(name_buffer, interface_name().c_str()); - - // Write unsuccessfully. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = 5; - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - sizeof(name_buffer)), - SyscallFailsWithErrno(ENODEV)); - - // Read it back successfully, it's unchanged. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, interface_name().size() + 1); - EXPECT_STREQ(name_buffer, interface_name().c_str()); -} - -// Tests that setsockopt of zero-length string correctly unsets the previous -// value. -TEST_P(BindToDeviceTest, SetsockoptValidThenClear) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - // Write successfully. - strncpy(name_buffer, interface_name().c_str(), sizeof(name_buffer)); - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - sizeof(name_buffer)), - SyscallSucceeds()); - - // Read it back successfully. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, interface_name().size() + 1); - EXPECT_STREQ(name_buffer, interface_name().c_str()); - - // Clear it successfully. - name_buffer_size = 0; - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - name_buffer_size), - SyscallSucceeds()); - - // Read it back successfully, it's cleared. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, 0); -} - -// Tests that setsockopt of empty string correctly unsets the previous -// value. -TEST_P(BindToDeviceTest, SetsockoptValidThenClearWithNull) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - // Write successfully. - strncpy(name_buffer, interface_name().c_str(), sizeof(name_buffer)); - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - sizeof(name_buffer)), - SyscallSucceeds()); - - // Read it back successfully. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, interface_name().size() + 1); - EXPECT_STREQ(name_buffer, interface_name().c_str()); - - // Clear it successfully. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer[0] = 0; - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - name_buffer_size), - SyscallSucceeds()); - - // Read it back successfully, it's cleared. - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = sizeof(name_buffer); - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, 0); -} - -// Tests getsockopt with different buffer sizes. -TEST_P(BindToDeviceTest, GetsockoptDevice) { - char name_buffer[IFNAMSIZ * 2]; - socklen_t name_buffer_size; - - // Write successfully. - strncpy(name_buffer, interface_name().c_str(), sizeof(name_buffer)); - ASSERT_THAT(setsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, name_buffer, - sizeof(name_buffer)), - SyscallSucceeds()); - - // Read it back at various buffer sizes. - for (size_t i = 0; i <= sizeof(name_buffer); i++) { - memset(name_buffer, kIllegalIfnameChar, sizeof(name_buffer)); - name_buffer_size = i; - SCOPED_TRACE(absl::StrCat("Buffer size: ", i)); - // Linux only allows a buffer at least IFNAMSIZ, even if less would suffice - // for this interface name. - if (name_buffer_size >= IFNAMSIZ) { - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, &name_buffer_size), - SyscallSucceeds()); - EXPECT_EQ(name_buffer_size, interface_name().size() + 1); - EXPECT_STREQ(name_buffer, interface_name().c_str()); - } else { - EXPECT_THAT(getsockopt(socket_fd(), SOL_SOCKET, SO_BINDTODEVICE, - name_buffer, &name_buffer_size), - SyscallFailsWithErrno(EINVAL)); - EXPECT_EQ(name_buffer_size, i); - } - } -} - -INSTANTIATE_TEST_SUITE_P(BindToDeviceTest, BindToDeviceTest, - ::testing::Values(IPv4UDPUnboundSocket(0), - IPv4TCPUnboundSocket(0))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_bind_to_device_distribution.cc b/test/syscalls/linux/socket_bind_to_device_distribution.cc deleted file mode 100644 index f8a0a80f2..000000000 --- a/test/syscalls/linux/socket_bind_to_device_distribution.cc +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <linux/if_tun.h> -#include <net/if.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <atomic> -#include <cstdio> -#include <cstring> -#include <map> -#include <memory> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_bind_to_device_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -using std::string; -using std::vector; - -struct EndpointConfig { - std::string bind_to_device; - double expected_ratio; -}; - -struct DistributionTestCase { - std::string name; - std::vector<EndpointConfig> endpoints; -}; - -struct ListenerConnector { - TestAddress listener; - TestAddress connector; -}; - -// Test fixture for SO_BINDTODEVICE tests the distribution of packets received -// with varying SO_BINDTODEVICE settings. -class BindToDeviceDistributionTest - : public ::testing::TestWithParam< - ::testing::tuple<ListenerConnector, DistributionTestCase>> { - protected: - void SetUp() override { - printf("Testing case: %s, listener=%s, connector=%s\n", - ::testing::get<1>(GetParam()).name.c_str(), - ::testing::get<0>(GetParam()).listener.description.c_str(), - ::testing::get<0>(GetParam()).connector.description.c_str()); - ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) - << "CAP_NET_RAW is required to use SO_BINDTODEVICE"; - } -}; - -PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) { - switch (family) { - case AF_INET: - return static_cast<uint16_t>( - reinterpret_cast<sockaddr_in const*>(&addr)->sin_port); - case AF_INET6: - return static_cast<uint16_t>( - reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port); - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } -} - -PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) { - switch (family) { - case AF_INET: - reinterpret_cast<sockaddr_in*>(addr)->sin_port = port; - return NoError(); - case AF_INET6: - reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port; - return NoError(); - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } -} - -// Binds sockets to different devices and then creates many TCP connections. -// Checks that the distribution of connections received on the sockets matches -// the expectation. -TEST_P(BindToDeviceDistributionTest, Tcp) { - auto const& [listener_connector, test] = GetParam(); - - TestAddress const& listener = listener_connector.listener; - TestAddress const& connector = listener_connector.connector; - sockaddr_storage listen_addr = listener.addr; - sockaddr_storage conn_addr = connector.addr; - - auto interface_names = GetInterfaceNames(); - - // Create the listening sockets. - std::vector<FileDescriptor> listener_fds; - std::vector<std::unique_ptr<Tunnel>> all_tunnels; - for (auto const& endpoint : test.endpoints) { - if (!endpoint.bind_to_device.empty() && - interface_names.find(endpoint.bind_to_device) == - interface_names.end()) { - all_tunnels.push_back( - ASSERT_NO_ERRNO_AND_VALUE(Tunnel::New(endpoint.bind_to_device))); - interface_names.insert(endpoint.bind_to_device); - } - - listener_fds.push_back(ASSERT_NO_ERRNO_AND_VALUE( - Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP))); - int fd = listener_fds.back().get(); - - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, - endpoint.bind_to_device.c_str(), - endpoint.bind_to_device.size() + 1), - SyscallSucceeds()); - ASSERT_THAT( - bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len), - SyscallSucceeds()); - ASSERT_THAT(listen(fd, 40), SyscallSucceeds()); - - // On the first bind we need to determine which port was bound. - if (listener_fds.size() > 1) { - continue; - } - - // Get the port bound by the listening socket. - socklen_t addrlen = listener.addr_len; - ASSERT_THAT( - getsockname(listener_fds[0].get(), - reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - } - - constexpr int kConnectAttempts = 10000; - std::atomic<int> connects_received = ATOMIC_VAR_INIT(0); - std::vector<int> accept_counts(listener_fds.size(), 0); - std::vector<std::unique_ptr<ScopedThread>> listen_threads( - listener_fds.size()); - - for (long unsigned int i = 0; i < listener_fds.size(); i++) { - listen_threads[i] = absl::make_unique<ScopedThread>( - [&listener_fds, &accept_counts, &connects_received, i, - kConnectAttempts]() { - do { - auto fd = Accept(listener_fds[i].get(), nullptr, nullptr); - if (!fd.ok()) { - // Another thread has shutdown our read side causing the accept to - // fail. - ASSERT_GE(connects_received, kConnectAttempts) - << "errno = " << fd.error(); - return; - } - // Receive some data from a socket to be sure that the connect() - // system call has been completed on another side. - // Do a short read and then close the socket to trigger a RST. This - // ensures that both ends of the connection are cleaned up and no - // goroutines hang around in TIME-WAIT. We do this so that this test - // does not timeout under gotsan runs where lots of goroutines can - // cause the test to use absurd amounts of memory. - // - // See: https://tools.ietf.org/html/rfc2525#page-50 section 2.17 - uint16_t data; - EXPECT_THAT( - RetryEINTR(recv)(fd.ValueOrDie().get(), &data, sizeof(data), 0), - SyscallSucceedsWithValue(sizeof(data))); - accept_counts[i]++; - } while (++connects_received < kConnectAttempts); - - // Shutdown all sockets to wake up other threads. - for (auto const& listener_fd : listener_fds) { - shutdown(listener_fd.get(), SHUT_RDWR); - } - }); - } - - for (int32_t i = 0; i < kConnectAttempts; i++) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT( - RetryEINTR(connect)(fd.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceeds()); - - EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - } - - // Join threads to be sure that all connections have been counted. - for (auto const& listen_thread : listen_threads) { - listen_thread->Join(); - } - // Check that connections are distributed correctly among listening sockets. - for (long unsigned int i = 0; i < accept_counts.size(); i++) { - EXPECT_THAT( - accept_counts[i], - EquivalentWithin(static_cast<int>(kConnectAttempts * - test.endpoints[i].expected_ratio), - 0.10)) - << "endpoint " << i << " got the wrong number of packets"; - } -} - -// Binds sockets to different devices and then sends many UDP packets. Checks -// that the distribution of packets received on the sockets matches the -// expectation. -TEST_P(BindToDeviceDistributionTest, Udp) { - auto const& [listener_connector, test] = GetParam(); - - TestAddress const& listener = listener_connector.listener; - TestAddress const& connector = listener_connector.connector; - sockaddr_storage listen_addr = listener.addr; - sockaddr_storage conn_addr = connector.addr; - - auto interface_names = GetInterfaceNames(); - - // Create the listening socket. - std::vector<FileDescriptor> listener_fds; - std::vector<std::unique_ptr<Tunnel>> all_tunnels; - for (auto const& endpoint : test.endpoints) { - if (!endpoint.bind_to_device.empty() && - interface_names.find(endpoint.bind_to_device) == - interface_names.end()) { - all_tunnels.push_back( - ASSERT_NO_ERRNO_AND_VALUE(Tunnel::New(endpoint.bind_to_device))); - interface_names.insert(endpoint.bind_to_device); - } - - listener_fds.push_back( - ASSERT_NO_ERRNO_AND_VALUE(Socket(listener.family(), SOCK_DGRAM, 0))); - int fd = listener_fds.back().get(); - - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, - endpoint.bind_to_device.c_str(), - endpoint.bind_to_device.size() + 1), - SyscallSucceeds()); - ASSERT_THAT( - bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len), - SyscallSucceeds()); - - // On the first bind we need to determine which port was bound. - if (listener_fds.size() > 1) { - continue; - } - - // Get the port bound by the listening socket. - socklen_t addrlen = listener.addr_len; - ASSERT_THAT( - getsockname(listener_fds[0].get(), - reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port)); - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - } - - constexpr int kConnectAttempts = 10000; - std::atomic<int> packets_received = ATOMIC_VAR_INIT(0); - std::vector<int> packets_per_socket(listener_fds.size(), 0); - std::vector<std::unique_ptr<ScopedThread>> receiver_threads( - listener_fds.size()); - - for (long unsigned int i = 0; i < listener_fds.size(); i++) { - receiver_threads[i] = absl::make_unique<ScopedThread>( - [&listener_fds, &packets_per_socket, &packets_received, i]() { - do { - struct sockaddr_storage addr = {}; - socklen_t addrlen = sizeof(addr); - int data; - - auto ret = RetryEINTR(recvfrom)( - listener_fds[i].get(), &data, sizeof(data), 0, - reinterpret_cast<struct sockaddr*>(&addr), &addrlen); - - if (packets_received < kConnectAttempts) { - ASSERT_THAT(ret, SyscallSucceedsWithValue(sizeof(data))); - } - - if (ret != sizeof(data)) { - // Another thread may have shutdown our read side causing the - // recvfrom to fail. - break; - } - - packets_received++; - packets_per_socket[i]++; - - // A response is required to synchronize with the main thread, - // otherwise the main thread can send more than can fit into receive - // queues. - EXPECT_THAT(RetryEINTR(sendto)( - listener_fds[i].get(), &data, sizeof(data), 0, - reinterpret_cast<sockaddr*>(&addr), addrlen), - SyscallSucceedsWithValue(sizeof(data))); - } while (packets_received < kConnectAttempts); - - // Shutdown all sockets to wake up other threads. - for (auto const& listener_fd : listener_fds) { - shutdown(listener_fd.get(), SHUT_RDWR); - } - }); - } - - for (int i = 0; i < kConnectAttempts; i++) { - FileDescriptor const fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(connector.family(), SOCK_DGRAM, 0)); - EXPECT_THAT(RetryEINTR(sendto)(fd.get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceedsWithValue(sizeof(i))); - int data; - EXPECT_THAT(RetryEINTR(recv)(fd.get(), &data, sizeof(data), 0), - SyscallSucceedsWithValue(sizeof(data))); - } - - // Join threads to be sure that all connections have been counted. - for (auto const& receiver_thread : receiver_threads) { - receiver_thread->Join(); - } - // Check that packets are distributed correctly among listening sockets. - for (long unsigned int i = 0; i < packets_per_socket.size(); i++) { - EXPECT_THAT( - packets_per_socket[i], - EquivalentWithin(static_cast<int>(kConnectAttempts * - test.endpoints[i].expected_ratio), - 0.10)) - << "endpoint " << i << " got the wrong number of packets"; - } -} - -std::vector<DistributionTestCase> GetDistributionTestCases() { - return std::vector<DistributionTestCase>{ - {"Even distribution among sockets not bound to device", - {{"", 1. / 3}, {"", 1. / 3}, {"", 1. / 3}}}, - {"Sockets bound to other interfaces get no packets", - {{"eth1", 0}, {"", 1. / 2}, {"", 1. / 2}}}, - {"Bound has priority over unbound", {{"eth1", 0}, {"", 0}, {"lo", 1}}}, - {"Even distribution among sockets bound to device", - {{"eth1", 0}, {"lo", 1. / 2}, {"lo", 1. / 2}}}, - }; -} - -INSTANTIATE_TEST_SUITE_P( - BindToDeviceTest, BindToDeviceDistributionTest, - ::testing::Combine(::testing::Values( - // Listeners bound to IPv4 addresses refuse - // connections using IPv6 addresses. - ListenerConnector{V4Any(), V4Loopback()}, - ListenerConnector{V4Loopback(), V4MappedLoopback()}), - ::testing::ValuesIn(GetDistributionTestCases()))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_bind_to_device_sequence.cc b/test/syscalls/linux/socket_bind_to_device_sequence.cc deleted file mode 100644 index d3cc71dbf..000000000 --- a/test/syscalls/linux/socket_bind_to_device_sequence.cc +++ /dev/null @@ -1,513 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <linux/capability.h> -#include <linux/if_tun.h> -#include <net/if.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> -#include <cstring> -#include <map> -#include <memory> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/container/node_hash_map.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_bind_to_device_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -using std::string; -using std::vector; - -// Test fixture for SO_BINDTODEVICE tests the results of sequences of socket -// binding. -class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> { - protected: - void SetUp() override { - printf("Testing case: %s\n", GetParam().description.c_str()); - ASSERT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) - << "CAP_NET_RAW is required to use SO_BINDTODEVICE"; - socket_factory_ = GetParam(); - - interface_names_ = GetInterfaceNames(); - } - - PosixErrorOr<std::unique_ptr<FileDescriptor>> NewSocket() const { - return socket_factory_.Create(); - } - - // 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) { - auto device = devices_.find(device_id); - if (device != devices_.end()) { - *device_name = device->second; - return; - } - - // Need to pick a new device. Try ethernet first. - *device_name = absl::StrCat("eth", next_unused_eth_); - if (interface_names_.find(*device_name) != interface_names_.end()) { - devices_[device_id] = *device_name; - next_unused_eth_++; - return; - } - - // Need to make a new tunnel device. gVisor tests should have enough - // ethernet devices to never reach here. - ASSERT_FALSE(IsRunningOnGvisor()); - // Need a tunnel. - tunnels_.push_back(ASSERT_NO_ERRNO_AND_VALUE(Tunnel::New())); - devices_[device_id] = tunnels_.back()->GetName(); - *device_name = devices_[device_id]; - } - - // Release the socket - void ReleaseSocket(int socket_id) { - // Close the socket that was made in a previous action. The socket_id - // indicates which socket to close based on index into the list of actions. - sockets_to_close_.erase(socket_id); - } - - // SetDevice changes the bind_to_device option. It does not bind or re-bind. - void SetDevice(int socket_id, int device_id) { - auto socket_fd = sockets_to_close_[socket_id]->get(); - string device_name; - ASSERT_NO_FATAL_FAILURE(GetDevice(device_id, &device_name)); - EXPECT_THAT(setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, - device_name.c_str(), device_name.size() + 1), - SyscallSucceedsWithValue(0)); - } - - // Bind a socket with the reuse options and bind_to_device options. Checks - // that all steps succeed and that the bind command's error matches want. - // 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) { - 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(); - if (socket_id != nullptr) { - *socket_id = next_socket_id_; - } - - // If reuse_port is indicated, do that. - if (reuse_port) { - EXPECT_THAT(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - } - - // If reuse_addr is indicated, do that. - if (reuse_addr) { - EXPECT_THAT(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - } - - // If the device is non-zero, bind to that device. - if (device_id != 0) { - string device_name; - ASSERT_NO_FATAL_FAILURE(GetDevice(device_id, &device_name)); - EXPECT_THAT(setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, - device_name.c_str(), device_name.size() + 1), - SyscallSucceedsWithValue(0)); - char get_device[100]; - socklen_t get_device_size = 100; - EXPECT_THAT(getsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, get_device, - &get_device_size), - SyscallSucceedsWithValue(0)); - } - - struct sockaddr_in addr = {}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = port_; - if (want == 0) { - ASSERT_THAT( - bind(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); - } else { - ASSERT_THAT( - bind(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr), - sizeof(addr)), - SyscallFailsWithErrno(want)); - } - - if (port_ == 0) { - // We don't yet know what port we'll be using so we need to fetch it and - // remember it for future commands. - socklen_t addr_size = sizeof(addr); - ASSERT_THAT( - getsockname(socket_fd, reinterpret_cast<struct sockaddr*>(&addr), - &addr_size), - SyscallSucceeds()); - port_ = addr.sin_port; - } - } - - private: - SocketKind socket_factory_; - // devices maps from the device id in the test case to the name of the device. - absl::node_hash_map<int, string> devices_; - // These are the tunnels that were created for the test and will be destroyed - // by the destructor. - vector<std::unique_ptr<Tunnel>> tunnels_; - // A list of all interface names before the test started. - std::unordered_set<string> interface_names_; - // The next ethernet device to use when requested a device. - int next_unused_eth_ = 1; - // The port for all tests. Originally 0 (any) and later set to the port that - // all further commands will use. - in_port_t port_ = 0; - // sockets_to_close_ is a map from action index to the socket that was - // created. - absl::node_hash_map<int, - std::unique_ptr<gvisor::testing::FileDescriptor>> - sockets_to_close_; - int next_socket_id_ = 0; -}; - -TEST_P(BindToDeviceSequenceTest, BindTwiceWithDeviceFails) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 3)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 3, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindToDevice) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 1)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 2)); -} - -TEST_P(BindToDeviceSequenceTest, BindToDeviceAndThenWithoutDevice) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindWithoutDevice) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindWithDevice) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123, 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 456, 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 789, 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindWithReuse) { - ASSERT_NO_FATAL_FAILURE( - BindSocket(/* reusePort */ true, /* reuse_addr */ false)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, - /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 0)); -} - -TEST_P(BindToDeviceSequenceTest, BindingWithReuseAndDevice) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 456)); - ASSERT_NO_FATAL_FAILURE( - BindSocket(/* reuse_port */ true, /* reuse_addr */ false)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 789)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 999, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, MixingReuseAndNotReuseByBindingToDevice) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 123, 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 456, 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 789, 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 999, 0)); -} - -TEST_P(BindToDeviceSequenceTest, CannotBindTo0AfterMixingReuseAndNotReuse) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 456)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindAndRelease) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123)); - int to_release; - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, 0, &to_release)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 345, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 789)); - // Release the bind to device 0 and try again. - ASSERT_NO_FATAL_FAILURE(ReleaseSocket(to_release)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 345)); -} - -TEST_P(BindToDeviceSequenceTest, BindTwiceWithReuseOnce) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindWithReuseAddr) { - ASSERT_NO_FATAL_FAILURE( - BindSocket(/* reusePort */ false, /* reuse_addr */ true)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 123, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 0)); -} - -TEST_P(BindToDeviceSequenceTest, - CannotBindTo0AfterMixingReuseAddrAndNotReuseAddr) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 123)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 456)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ true, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindReuseAddrReusePortThenReusePort) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ true, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindReuseAddrReusePortThenReuseAddr) { - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindDoubleReuseAddrReusePortThenReusePort) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ true, /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ true, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindDoubleReuseAddrReusePortThenReuseAddr) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ true, /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindReusePortThenReuseAddrReusePort) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ true, /* reuse_addr */ false, /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ true, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, BindReuseAddrThenReuseAddr) { - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0, EADDRINUSE)); -} - -TEST_P(BindToDeviceSequenceTest, - BindReuseAddrThenReuseAddrReusePortThenReuseAddr) { - // The behavior described in this test seems like a Linux bug. It doesn't - // make any sense and it is unlikely that any applications rely on it. - // - // Both SO_REUSEADDR and SO_REUSEPORT allow binding multiple UDP sockets to - // the same address and deliver each packet to exactly one of the bound - // sockets. If both are enabled, one of the strategies is selected to route - // packets. The strategy is selected dynamically based on the settings of the - // currently bound sockets. Usually, the strategy is selected based on the - // common setting (SO_REUSEADDR or SO_REUSEPORT) amongst the sockets, but for - // some reason, Linux allows binding sets of sockets with no overlapping - // settings in some situations. In this case, it is not obvious which strategy - // would be selected as the configured setting is a contradiction. - SKIP_IF(IsRunningOnGvisor()); - - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ true, /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ true, - /* bind_to_device */ 0)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ true, - /* reuse_addr */ false, - /* bind_to_device */ 0)); -} - -// Repro test for gvisor.dev/issue/1217. Not replicated in ports_test.go as this -// test is different from the others and wouldn't fit well there. -TEST_P(BindToDeviceSequenceTest, BindAndReleaseDifferentDevice) { - int to_release; - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 3, 0, &to_release)); - ASSERT_NO_FATAL_FAILURE(BindSocket(/* reuse_port */ false, - /* reuse_addr */ false, - /* bind_to_device */ 3, EADDRINUSE)); - // Change the device. Since the socket was already bound, this should have no - // effect. - SetDevice(to_release, 2); - // Release the bind to device 3 and try again. - ASSERT_NO_FATAL_FAILURE(ReleaseSocket(to_release)); - ASSERT_NO_FATAL_FAILURE(BindSocket( - /* reuse_port */ false, /* reuse_addr */ false, /* bind_to_device */ 3)); -} - -INSTANTIATE_TEST_SUITE_P(BindToDeviceTest, BindToDeviceSequenceTest, - ::testing::Values(IPv4UDPUnboundSocket(0), - IPv4TCPUnboundSocket(0))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_bind_to_device_util.cc b/test/syscalls/linux/socket_bind_to_device_util.cc deleted file mode 100644 index f4ee775bd..000000000 --- a/test/syscalls/linux/socket_bind_to_device_util.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2019 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/syscalls/linux/socket_bind_to_device_util.h" - -#include <arpa/inet.h> -#include <fcntl.h> -#include <linux/if_tun.h> -#include <net/if.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <cstdio> -#include <cstring> -#include <map> -#include <memory> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -using std::string; - -PosixErrorOr<std::unique_ptr<Tunnel>> Tunnel::New(string tunnel_name) { - int fd; - RETURN_ERROR_IF_SYSCALL_FAIL(fd = open("/dev/net/tun", O_RDWR)); - - // Using `new` to access a non-public constructor. - auto new_tunnel = absl::WrapUnique(new Tunnel(fd)); - - ifreq ifr = {}; - ifr.ifr_flags = IFF_TUN; - strncpy(ifr.ifr_name, tunnel_name.c_str(), sizeof(ifr.ifr_name)); - - RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(fd, TUNSETIFF, &ifr)); - new_tunnel->name_ = ifr.ifr_name; - return new_tunnel; -} - -std::unordered_set<string> GetInterfaceNames() { - struct if_nameindex* interfaces = if_nameindex(); - std::unordered_set<string> names; - if (interfaces == nullptr) { - return names; - } - for (auto interface = interfaces; - interface->if_index != 0 || interface->if_name != nullptr; interface++) { - names.insert(interface->if_name); - } - if_freenameindex(interfaces); - return names; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_bind_to_device_util.h b/test/syscalls/linux/socket_bind_to_device_util.h deleted file mode 100644 index f941ccc86..000000000 --- a/test/syscalls/linux/socket_bind_to_device_util.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2019 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_SYSCALLS_SOCKET_BIND_TO_DEVICE_UTILS_H_ -#define GVISOR_TEST_SYSCALLS_SOCKET_BIND_TO_DEVICE_UTILS_H_ - -#include <arpa/inet.h> -#include <linux/if_tun.h> -#include <net/if.h> -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <cstdio> -#include <cstring> -#include <map> -#include <memory> -#include <string> -#include <unordered_map> -#include <unordered_set> -#include <utility> -#include <vector> - -#include "absl/memory/memory.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -class Tunnel { - public: - static PosixErrorOr<std::unique_ptr<Tunnel>> New( - std::string tunnel_name = ""); - const std::string& GetName() const { return name_; } - - ~Tunnel() { - if (fd_ != -1) { - close(fd_); - } - } - - private: - Tunnel(int fd) : fd_(fd) {} - int fd_ = -1; - std::string name_; -}; - -std::unordered_set<std::string> GetInterfaceNames(); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_SOCKET_BIND_TO_DEVICE_UTILS_H_ diff --git a/test/syscalls/linux/socket_blocking.cc b/test/syscalls/linux/socket_blocking.cc deleted file mode 100644 index 7e88aa2d9..000000000 --- a/test/syscalls/linux/socket_blocking.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_blocking.h" - -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -TEST_P(BlockingSocketPairTest, RecvBlocks) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[100]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - constexpr auto kDuration = absl::Milliseconds(200); - auto before = Now(CLOCK_MONOTONIC); - - const ScopedThread t([&]() { - absl::SleepFor(kDuration); - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - }); - - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - auto after = Now(CLOCK_MONOTONIC); - EXPECT_GE(after - before, kDuration); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_blocking.h b/test/syscalls/linux/socket_blocking.h deleted file mode 100644 index db26e5ef5..000000000 --- a/test/syscalls/linux/socket_blocking.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_BLOCKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_BLOCKING_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of blocking connected sockets. -using BlockingSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_capability.cc b/test/syscalls/linux/socket_capability.cc deleted file mode 100644 index 84b5b2b21..000000000 --- a/test/syscalls/linux/socket_capability.cc +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Subset of socket tests that need Linux-specific headers (compared to POSIX -// headers). - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -TEST(SocketTest, UnixConnectNeedsWritePerm) { - SKIP_IF(IsRunningWithVFS1()); - - FileDescriptor bound = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX)); - - struct sockaddr_un addr = - ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(/*abstract=*/false, AF_UNIX)); - ASSERT_THAT(bind(bound.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); - ASSERT_THAT(listen(bound.get(), 1), SyscallSucceeds()); - - // Drop capabilites that allow us to override permision checks. Otherwise if - // the test is run as root, the connect below will bypass permission checks - // and succeed unexpectedly. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - // Connect should fail without write perms. - ASSERT_THAT(chmod(addr.sun_path, 0500), SyscallSucceeds()); - FileDescriptor client = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX)); - ASSERT_THAT(connect(client.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallFailsWithErrno(EACCES)); - - // Connect should succeed with write perms. - ASSERT_THAT(chmod(addr.sun_path, 0200), SyscallSucceeds()); - EXPECT_THAT(connect(client.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_filesystem.cc b/test/syscalls/linux/socket_filesystem.cc deleted file mode 100644 index 287359363..000000000 --- a/test/syscalls/linux/socket_filesystem.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_generic.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/socket_unix.h" -#include "test/syscalls/linux/socket_unix_cmsg.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVec<SocketPairKind>( - FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P( - FilesystemUnixSockets, AllSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - FilesystemUnixSockets, UnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - FilesystemUnixSockets, UnixSocketPairCmsgTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_generic.h b/test/syscalls/linux/socket_generic.h deleted file mode 100644 index 00ae7bfc3..000000000 --- a/test/syscalls/linux/socket_generic.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of blocking and non-blocking -// connected stream sockets. -using AllSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_ diff --git a/test/syscalls/linux/socket_generic_stress.cc b/test/syscalls/linux/socket_generic_stress.cc deleted file mode 100644 index c35aa2183..000000000 --- a/test/syscalls/linux/socket_generic_stress.cc +++ /dev/null @@ -1,299 +0,0 @@ -// 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 <poll.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> - -#include <array> -#include <string> - -#include "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -constexpr char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range"; - -PosixErrorOr<int> NumPorts() { - int min = 0; - int max = 1 << 16; - - // Read the ephemeral range from /proc. - ASSIGN_OR_RETURN_ERRNO(std::string rangefile, GetContents(kRangeFile)); - const std::string err_msg = - absl::StrFormat("%s has invalid content: %s", kRangeFile, rangefile); - if (rangefile.back() != '\n') { - return PosixError(EINVAL, err_msg); - } - rangefile.pop_back(); - std::vector<std::string> range = - absl::StrSplit(rangefile, absl::ByAnyChar("\t ")); - if (range.size() < 2 || !absl::SimpleAtoi(range.front(), &min) || - !absl::SimpleAtoi(range.back(), &max)) { - return PosixError(EINVAL, err_msg); - } - - // If we can open as writable, limit the range. - if (!access(kRangeFile, W_OK)) { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, - Open(kRangeFile, O_WRONLY | O_TRUNC, 0)); - max = min + 50; - const std::string small_range = absl::StrFormat("%d %d", min, max); - int n = write(fd.get(), small_range.c_str(), small_range.size()); - if (n < 0) { - return PosixError( - errno, - absl::StrFormat("write(%d [%s], \"%s\", %d)", fd.get(), kRangeFile, - small_range.c_str(), small_range.size())); - } - } - return max - min; -} - -// Test fixture for tests that apply to pairs of connected sockets. -using ConnectStressTest = SocketPairTest; - -TEST_P(ConnectStressTest, Reset) { - const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts()); - for (int i = 0; i < nports * 2; i++) { - const std::unique_ptr<SocketPair> sockets = - ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Send some data to ensure that the connection gets reset and the port gets - // released immediately. This avoids either end entering TIME-WAIT. - char sent_data[100] = {}; - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - // Poll the other FD to make sure that the data is in the receive buffer - // before closing it to ensure a RST is triggered. - const int kTimeout = 10000; - struct pollfd pfd = { - .fd = sockets->second_fd(), - .events = POLL_IN, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - } -} - -// Tests that opening too many connections -- without closing them -- does lead -// to port exhaustion. -TEST_P(ConnectStressTest, TooManyOpen) { - const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts()); - int err_num = 0; - std::vector<std::unique_ptr<SocketPair>> sockets = - std::vector<std::unique_ptr<SocketPair>>(nports); - for (int i = 0; i < nports * 2; i++) { - PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair(); - if (!socks.ok()) { - err_num = socks.error().errno_value(); - break; - } - sockets.push_back(std::move(socks).ValueOrDie()); - } - ASSERT_EQ(err_num, EADDRINUSE); -} - -INSTANTIATE_TEST_SUITE_P( - AllConnectedSockets, ConnectStressTest, - ::testing::Values(IPv6UDPBidirectionalBindSocketPair(0), - IPv4UDPBidirectionalBindSocketPair(0), - DualStackUDPBidirectionalBindSocketPair(0), - - // Without REUSEADDR, we get port exhaustion on Linux. - SetSockOpt(SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn)(IPv6TCPAcceptBindSocketPair(0)), - SetSockOpt(SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn)(IPv4TCPAcceptBindSocketPair(0)), - SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)( - DualStackTCPAcceptBindSocketPair(0)))); - -// Test fixture for tests that apply to pairs of connected sockets created with -// a persistent listener (if applicable). -class PersistentListenerConnectStressTest : public SocketPairTest { - protected: - PersistentListenerConnectStressTest() : slept_{false} {} - - // NewSocketSleep is the same as NewSocketPair, but will sleep once (over the - // lifetime of the fixture) and retry if creation fails due to EADDRNOTAVAIL. - PosixErrorOr<std::unique_ptr<SocketPair>> NewSocketSleep() { - // We can't reuse a connection too close in time to its last use, as TCP - // uses the timestamp difference to disambiguate connections. With a - // sufficiently small port range, we'll cycle through too quickly, and TCP - // won't allow for connection reuse. Thus, we sleep the first time - // encountering EADDRINUSE to allow for that difference (1 second in - // gVisor). - PosixErrorOr<std::unique_ptr<SocketPair>> socks = NewSocketPair(); - if (socks.ok()) { - return socks; - } - if (!slept_ && socks.error().errno_value() == EADDRNOTAVAIL) { - absl::SleepFor(absl::Milliseconds(1500)); - slept_ = true; - return NewSocketPair(); - } - return socks; - } - - private: - bool slept_; -}; - -TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) { - const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts()); - for (int i = 0; i < nports * 2; i++) { - std::unique_ptr<SocketPair> sockets = - ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep()); - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds()); - if (GetParam().type == SOCK_STREAM) { - // Poll the other FD to make sure that we see the FIN from the other - // side before closing the second_fd. This ensures that the first_fd - // enters TIME-WAIT and not second_fd. - const int kTimeout = 10000; - struct pollfd pfd = { - .fd = sockets->second_fd(), - .events = POLL_IN, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - } - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds()); - } -} - -TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) { - const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts()); - for (int i = 0; i < nports * 2; i++) { - const std::unique_ptr<SocketPair> sockets = - ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds()); - if (GetParam().type == SOCK_STREAM) { - // Poll the other FD to make sure that we see the FIN from the other - // side before closing the first_fd. This ensures that the second_fd - // enters TIME-WAIT and not first_fd. - const int kTimeout = 10000; - struct pollfd pfd = { - .fd = sockets->first_fd(), - .events = POLL_IN, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - } - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds()); - } -} - -TEST_P(PersistentListenerConnectStressTest, Close) { - const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts()); - for (int i = 0; i < nports * 2; i++) { - std::unique_ptr<SocketPair> sockets = - ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep()); - } -} - -INSTANTIATE_TEST_SUITE_P( - AllConnectedSockets, PersistentListenerConnectStressTest, - ::testing::Values( - IPv6UDPBidirectionalBindSocketPair(0), - IPv4UDPBidirectionalBindSocketPair(0), - DualStackUDPBidirectionalBindSocketPair(0), - - // Without REUSEADDR, we get port exhaustion on Linux. - SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)( - IPv6TCPAcceptBindPersistentListenerSocketPair(0)), - SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)( - IPv4TCPAcceptBindPersistentListenerSocketPair(0)), - SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)( - DualStackTCPAcceptBindPersistentListenerSocketPair(0)))); - -using DataTransferStressTest = SocketPairTest; - -TEST_P(DataTransferStressTest, BigDataTransfer) { - // TODO(b/165912341): These are too slow on KVM platform with nested virt. - SKIP_IF(GvisorPlatform() == Platform::kKVM); - - const std::unique_ptr<SocketPair> sockets = - ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int client_fd = sockets->first_fd(); - int server_fd = sockets->second_fd(); - - ScopedThread echo([server_fd]() { - std::array<uint8_t, 1024> buf; - for (;;) { - ssize_t r = read(server_fd, buf.data(), buf.size()); - ASSERT_THAT(r, SyscallSucceeds()); - if (r == 0) { - break; - } - for (size_t i = 0; i < r;) { - ssize_t w = write(server_fd, buf.data() + i, r - i); - ASSERT_GE(w, 0); - i += w; - } - } - ASSERT_THAT(shutdown(server_fd, SHUT_WR), SyscallSucceeds()); - }); - - const std::string chunk = "Though this upload be but little, it is fierce."; - std::string big_string; - while (big_string.size() < 31 << 20) { - big_string += chunk; - } - absl::string_view data = big_string; - - ScopedThread writer([client_fd, data]() { - absl::string_view view = data; - while (!view.empty()) { - ssize_t n = write(client_fd, view.data(), view.size()); - ASSERT_GE(n, 0); - view = view.substr(n); - } - ASSERT_THAT(shutdown(client_fd, SHUT_WR), SyscallSucceeds()); - }); - - std::string buf; - buf.resize(1 << 20); - while (!data.empty()) { - ssize_t n = read(client_fd, buf.data(), buf.size()); - ASSERT_GE(n, 0); - for (size_t i = 0; i < n; i += chunk.size()) { - size_t c = std::min(chunk.size(), n - i); - ASSERT_EQ(buf.substr(i, c), data.substr(i, c)) << "offset " << i; - } - data = data.substr(n); - } - // Should read EOF now. - ASSERT_THAT(read(client_fd, buf.data(), buf.size()), - SyscallSucceedsWithValue(0)); -} - -INSTANTIATE_TEST_SUITE_P( - AllConnectedSockets, DataTransferStressTest, - ::testing::Values(IPv6TCPAcceptBindPersistentListenerSocketPair(0), - IPv4TCPAcceptBindPersistentListenerSocketPair(0), - DualStackTCPAcceptBindPersistentListenerSocketPair(0))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_generic_test_cases.cc b/test/syscalls/linux/socket_generic_test_cases.cc deleted file mode 100644 index 5c4cb6c35..000000000 --- a/test/syscalls/linux/socket_generic_test_cases.cc +++ /dev/null @@ -1,903 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_generic.h" - -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -// This file is a generic socket test file. It must be built with another file -// that provides the test types. - -namespace gvisor { -namespace testing { - -TEST_P(AllSocketPairTest, BasicReadWrite) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char buf[20]; - const std::string data = "abc"; - ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), 3), - SyscallSucceedsWithValue(3)); - ASSERT_THAT(ReadFd(sockets->second_fd(), buf, 3), - SyscallSucceedsWithValue(3)); - EXPECT_EQ(data, absl::string_view(buf, 3)); -} - -TEST_P(AllSocketPairTest, BasicReadWriteBadBuffer) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - const std::string data = "abc"; - ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), 3), - SyscallSucceedsWithValue(3)); - ASSERT_THAT(ReadFd(sockets->second_fd(), nullptr, 3), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_P(AllSocketPairTest, BasicSendRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(AllSocketPairTest, BasicSendmmsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[200]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - std::vector<struct mmsghdr> msgs(10); - std::vector<struct iovec> iovs(msgs.size()); - const int chunk_size = sizeof(sent_data) / msgs.size(); - for (size_t i = 0; i < msgs.size(); i++) { - iovs[i].iov_len = chunk_size; - iovs[i].iov_base = &sent_data[i * chunk_size]; - msgs[i].msg_hdr.msg_iov = &iovs[i]; - msgs[i].msg_hdr.msg_iovlen = 1; - } - - ASSERT_THAT( - RetryEINTR(sendmmsg)(sockets->first_fd(), &msgs[0], msgs.size(), 0), - SyscallSucceedsWithValue(msgs.size())); - - for (const struct mmsghdr& msg : msgs) { - EXPECT_EQ(chunk_size, msg.msg_len); - } - - char received_data[sizeof(sent_data)]; - for (size_t i = 0; i < msgs.size(); i++) { - ASSERT_THAT(ReadFd(sockets->second_fd(), &received_data[i * chunk_size], - chunk_size), - SyscallSucceedsWithValue(chunk_size)); - } - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(AllSocketPairTest, SendmmsgIsLimitedByMAXIOV) { - std::unique_ptr<SocketPair> sockets = - ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char c = 0; - - std::vector<struct mmsghdr> msgs(UIO_MAXIOV + 1); - std::vector<struct iovec> iovs(msgs.size()); - for (size_t i = 0; i < msgs.size(); i++) { - iovs[i].iov_len = 1; - iovs[i].iov_base = &c; - msgs[i].msg_hdr.msg_iov = &iovs[i]; - msgs[i].msg_hdr.msg_iovlen = 1; - } - - int n; - ASSERT_THAT(n = RetryEINTR(sendmmsg)(sockets->first_fd(), msgs.data(), - msgs.size(), MSG_DONTWAIT), - SyscallSucceeds()); - EXPECT_LE(n, UIO_MAXIOV); -} - -TEST_P(AllSocketPairTest, BasicRecvmmsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[200]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - char received_data[sizeof(sent_data)]; - std::vector<struct mmsghdr> msgs(10); - std::vector<struct iovec> iovs(msgs.size()); - const int chunk_size = sizeof(sent_data) / msgs.size(); - for (size_t i = 0; i < msgs.size(); i++) { - iovs[i].iov_len = chunk_size; - iovs[i].iov_base = &received_data[i * chunk_size]; - msgs[i].msg_hdr.msg_iov = &iovs[i]; - msgs[i].msg_hdr.msg_iovlen = 1; - } - - for (size_t i = 0; i < msgs.size(); i++) { - ASSERT_THAT( - WriteFd(sockets->first_fd(), &sent_data[i * chunk_size], chunk_size), - SyscallSucceedsWithValue(chunk_size)); - } - - ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->second_fd(), &msgs[0], msgs.size(), - 0, nullptr), - SyscallSucceedsWithValue(msgs.size())); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - for (const struct mmsghdr& msg : msgs) { - EXPECT_EQ(chunk_size, msg.msg_len); - } -} - -TEST_P(AllSocketPairTest, SendmsgRecvmsg10KB) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - std::vector<char> sent_data(10 * 1024); - RandomizeBuffer(sent_data.data(), sent_data.size()); - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size())); - - std::vector<char> received_data(sent_data.size()); - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->second_fd(), received_data.data(), - received_data.size())); - - EXPECT_EQ(0, - memcmp(sent_data.data(), received_data.data(), sent_data.size())); -} - -// This test validates that a sendmsg/recvmsg w/ MSG_CTRUNC is a no-op on -// input flags. -TEST_P(AllSocketPairTest, SendmsgRecvmsgMsgCtruncNoop) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - std::vector<char> sent_data(10 * 1024); - RandomizeBuffer(sent_data.data(), sent_data.size()); - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size())); - - std::vector<char> received_data(sent_data.size()); - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - iov.iov_base = &received_data[0]; - iov.iov_len = received_data.size(); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - // MSG_CTRUNC should be a no-op. - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CTRUNC), - SyscallSucceedsWithValue(received_data.size())); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - EXPECT_EQ(cmsg, nullptr); - EXPECT_EQ(msg.msg_controllen, 0); - EXPECT_EQ(0, - memcmp(sent_data.data(), received_data.data(), sent_data.size())); -} - -TEST_P(AllSocketPairTest, SendmsgRecvmsg16KB) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - std::vector<char> sent_data(16 * 1024); - RandomizeBuffer(sent_data.data(), sent_data.size()); - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data.data(), sent_data.size())); - - std::vector<char> received_data(sent_data.size()); - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->second_fd(), received_data.data(), - received_data.size())); - - EXPECT_EQ(0, - memcmp(sent_data.data(), received_data.data(), sent_data.size())); -} - -TEST_P(AllSocketPairTest, RecvmsgMsghdrFlagsNotClearedOnFailure) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char received_data[10] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); - - // Check that msghdr flags were not changed. - EXPECT_EQ(msg.msg_flags, -1); -} - -TEST_P(AllSocketPairTest, RecvmsgMsghdrFlagsCleared) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data)] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(sent_data))); - - // Check that msghdr flags were cleared. - EXPECT_EQ(msg.msg_flags, 0); -} - -TEST_P(AllSocketPairTest, RecvmsgPeekMsghdrFlagsCleared) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data)] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_PEEK), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(sent_data))); - - // Check that msghdr flags were cleared. - EXPECT_EQ(msg.msg_flags, 0); -} - -TEST_P(AllSocketPairTest, RecvmsgIovNotUpdated) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data) * 2] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(sent_data))); - - // Check that the iovec length was not updated. - EXPECT_EQ(msg.msg_iov->iov_len, sizeof(received_data)); -} - -TEST_P(AllSocketPairTest, RecvmmsgInvalidTimeout) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char buf[10]; - struct mmsghdr msg = {}; - struct iovec iov = {}; - iov.iov_len = sizeof(buf); - iov.iov_base = buf; - msg.msg_hdr.msg_iov = &iov; - msg.msg_hdr.msg_iovlen = 1; - struct timespec timeout = {-1, -1}; - ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->first_fd(), &msg, 1, 0, &timeout), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(AllSocketPairTest, RecvmmsgTimeoutBeforeRecv) { - // There is a known bug in the Linux recvmmsg(2) causing it to block forever - // if the timeout expires while blocking for the first message. - SKIP_IF(!IsRunningOnGvisor()); - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char buf[10]; - struct mmsghdr msg = {}; - struct iovec iov = {}; - iov.iov_len = sizeof(buf); - iov.iov_base = buf; - msg.msg_hdr.msg_iov = &iov; - msg.msg_hdr.msg_iovlen = 1; - struct timespec timeout = {}; - ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->first_fd(), &msg, 1, 0, &timeout), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, MsgPeek) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[50]; - memset(&sent_data, 0, sizeof(sent_data)); - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data)]; - for (int i = 0; i < 3; i++) { - memset(received_data, 0, sizeof(received_data)); - EXPECT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_PEEK), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); - } - - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); -} - -TEST_P(AllSocketPairTest, LingerSocketOption) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - struct linger got_linger = {-1, -1}; - socklen_t length = sizeof(struct linger); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &length), - SyscallSucceedsWithValue(0)); - struct linger want_linger = {}; - EXPECT_EQ(0, memcmp(&want_linger, &got_linger, sizeof(struct linger))); - EXPECT_EQ(sizeof(struct linger), length); -} - -TEST_P(AllSocketPairTest, KeepAliveSocketOption) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int keepalive = -1; - socklen_t length = sizeof(int); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, - &keepalive, &length), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, keepalive); - EXPECT_EQ(sizeof(int), length); -} - -TEST_P(AllSocketPairTest, RcvBufSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int size = 0; - socklen_t size_size = sizeof(size); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &size, &size_size), - SyscallSucceeds()); - EXPECT_GT(size, 0); -} - -TEST_P(AllSocketPairTest, GetSndBufSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int size = 0; - socklen_t size_size = sizeof(size); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &size, &size_size), - SyscallSucceeds()); - EXPECT_GT(size, 0); -} - -TEST_P(AllSocketPairTest, RecvTimeoutReadSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutRecvSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutRecvOneSecondSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 1, .tv_usec = 0 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - struct msghdr msg = {}; - char buf[20] = {}; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, SendTimeoutDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - timeval actual_tv = {.tv_sec = -1, .tv_usec = -1}; - socklen_t len = sizeof(actual_tv); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, - &actual_tv, &len), - SyscallSucceeds()); - EXPECT_EQ(actual_tv.tv_sec, 0); - EXPECT_EQ(actual_tv.tv_usec, 0); -} - -TEST_P(AllSocketPairTest, SetGetSendTimeout) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // tv_usec should be a multiple of 4000 to work on most systems. - timeval tv = {.tv_sec = 89, .tv_usec = 44000}; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - timeval actual_tv = {}; - socklen_t len = sizeof(actual_tv); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, - &actual_tv, &len), - SyscallSucceeds()); - EXPECT_EQ(actual_tv.tv_sec, tv.tv_sec); - EXPECT_EQ(actual_tv.tv_usec, tv.tv_usec); -} - -TEST_P(AllSocketPairTest, SetGetSendTimeoutLargerArg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval_with_extra { - struct timeval tv; - int64_t extra_data; - } ABSL_ATTRIBUTE_PACKED; - - // tv_usec should be a multiple of 4000 to work on most systems. - timeval_with_extra tv_extra = { - .tv = {.tv_sec = 0, .tv_usec = 124000}, - }; - - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, - &tv_extra, sizeof(tv_extra)), - SyscallSucceeds()); - - timeval_with_extra actual_tv = {}; - socklen_t len = sizeof(actual_tv); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, - &actual_tv, &len), - SyscallSucceeds()); - EXPECT_EQ(actual_tv.tv.tv_sec, tv_extra.tv.tv_sec); - EXPECT_EQ(actual_tv.tv.tv_usec, tv_extra.tv.tv_usec); -} - -TEST_P(AllSocketPairTest, SendTimeoutAllowsWrite) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST_P(AllSocketPairTest, SendTimeoutAllowsSend) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST_P(AllSocketPairTest, SendTimeoutAllowsSendmsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - ASSERT_NO_FATAL_FAILURE(SendNullCmsg(sockets->first_fd(), buf, sizeof(buf))); -} - -TEST_P(AllSocketPairTest, RecvTimeoutDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - timeval actual_tv = {.tv_sec = -1, .tv_usec = -1}; - socklen_t len = sizeof(actual_tv); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, - &actual_tv, &len), - SyscallSucceeds()); - EXPECT_EQ(actual_tv.tv_sec, 0); - EXPECT_EQ(actual_tv.tv_usec, 0); -} - -TEST_P(AllSocketPairTest, SetGetRecvTimeout) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - timeval tv = {.tv_sec = 123, .tv_usec = 456000}; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - timeval actual_tv = {}; - socklen_t len = sizeof(actual_tv); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, - &actual_tv, &len), - SyscallSucceeds()); - EXPECT_EQ(actual_tv.tv_sec, 123); - EXPECT_EQ(actual_tv.tv_usec, 456000); -} - -TEST_P(AllSocketPairTest, SetGetRecvTimeoutLargerArg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval_with_extra { - struct timeval tv; - int64_t extra_data; - } ABSL_ATTRIBUTE_PACKED; - - timeval_with_extra tv_extra = { - .tv = {.tv_sec = 0, .tv_usec = 432000}, - }; - - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, - &tv_extra, sizeof(tv_extra)), - SyscallSucceeds()); - - timeval_with_extra actual_tv = {}; - socklen_t len = sizeof(actual_tv); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, - &actual_tv, &len), - SyscallSucceeds()); - EXPECT_EQ(actual_tv.tv.tv_sec, 0); - EXPECT_EQ(actual_tv.tv.tv_usec, 432000); -} - -TEST_P(AllSocketPairTest, RecvTimeoutRecvmsgOneSecondSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 1, .tv_usec = 0 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - struct msghdr msg = {}; - char buf[20] = {}; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutUsecTooLarge) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 2000000 // 2 seconds. - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallFailsWithErrno(EDOM)); -} - -TEST_P(AllSocketPairTest, SendTimeoutUsecTooLarge) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 2000000 // 2 seconds. - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallFailsWithErrno(EDOM)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutUsecNeg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = -1 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallFailsWithErrno(EDOM)); -} - -TEST_P(AllSocketPairTest, SendTimeoutUsecNeg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = -1 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallFailsWithErrno(EDOM)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutNegSecRead) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = -1, .tv_usec = 0 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - EXPECT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = -1, .tv_usec = 0 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - char buf[20] = {}; - EXPECT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutNegSecRecvmsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = -1, .tv_usec = 0 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - struct msghdr msg = {}; - char buf[20] = {}; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - EXPECT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvWaitAll) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[100]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_WAITALL), - SyscallSucceedsWithValue(sizeof(sent_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(AllSocketPairTest, RecvWaitAllDontWait) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char data[100] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), data, sizeof(data), - MSG_WAITALL | MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(AllSocketPairTest, RecvTimeoutWaitAll) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 200000 // 200ms - }; - EXPECT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, - sizeof(tv)), - SyscallSucceeds()); - - char sent_data[100]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data) * 2] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_WAITALL), - SyscallSucceedsWithValue(sizeof(sent_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(AllSocketPairTest, GetSockoptType) { - int type = GetParam().type; - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - for (const int fd : {sockets->first_fd(), sockets->second_fd()}) { - int opt; - socklen_t optlen = sizeof(opt); - EXPECT_THAT(getsockopt(fd, SOL_SOCKET, SO_TYPE, &opt, &optlen), - SyscallSucceeds()); - - // Type may have SOCK_NONBLOCK and SOCK_CLOEXEC ORed into it. Remove these - // before comparison. - type &= ~(SOCK_NONBLOCK | SOCK_CLOEXEC); - EXPECT_EQ(opt, type) << absl::StrFormat( - "getsockopt(%d, SOL_SOCKET, SO_TYPE, &opt, &optlen) => opt=%d was " - "unexpected", - fd, opt); - } -} - -TEST_P(AllSocketPairTest, GetSockoptDomain) { - const int domain = GetParam().domain; - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - for (const int fd : {sockets->first_fd(), sockets->second_fd()}) { - int opt; - socklen_t optlen = sizeof(opt); - EXPECT_THAT(getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &opt, &optlen), - SyscallSucceeds()); - EXPECT_EQ(opt, domain) << absl::StrFormat( - "getsockopt(%d, SOL_SOCKET, SO_DOMAIN, &opt, &optlen) => opt=%d was " - "unexpected", - fd, opt); - } -} - -TEST_P(AllSocketPairTest, GetSockoptProtocol) { - const int protocol = GetParam().protocol; - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - for (const int fd : {sockets->first_fd(), sockets->second_fd()}) { - int opt; - socklen_t optlen = sizeof(opt); - EXPECT_THAT(getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &opt, &optlen), - SyscallSucceeds()); - EXPECT_EQ(opt, protocol) << absl::StrFormat( - "getsockopt(%d, SOL_SOCKET, SO_PROTOCOL, &opt, &optlen) => opt=%d was " - "unexpected", - fd, opt); - } -} - -TEST_P(AllSocketPairTest, SetAndGetBooleanSocketOptions) { - int sock_opts[] = {SO_BROADCAST, SO_PASSCRED, SO_NO_CHECK, - SO_REUSEADDR, SO_REUSEPORT, SO_KEEPALIVE}; - for (int sock_opt : sock_opts) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int enable = -1; - socklen_t enableLen = sizeof(enable); - - // Test that the option is initially set to false. - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, sock_opt, &enable, - &enableLen), - SyscallSucceeds()); - ASSERT_EQ(enableLen, sizeof(enable)); - EXPECT_EQ(enable, 0) << absl::StrFormat( - "getsockopt(fd, SOL_SOCKET, %d, &enable, &enableLen) => enable=%d", - sock_opt, enable); - - // Test that setting the option to true is reflected in the subsequent - // call to getsockopt(2). - enable = 1; - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, sock_opt, &enable, - sizeof(enable)), - SyscallSucceeds()); - enable = -1; - enableLen = sizeof(enable); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, sock_opt, &enable, - &enableLen), - SyscallSucceeds()); - ASSERT_EQ(enableLen, sizeof(enable)); - EXPECT_EQ(enable, 1) << absl::StrFormat( - "getsockopt(fd, SOL_SOCKET, %d, &enable, &enableLen) => enable=%d", - sock_opt, enable); - } -} - -TEST_P(AllSocketPairTest, GetSocketOutOfBandInlineOption) { - // We do not support disabling this option. It is always enabled. - SKIP_IF(!IsRunningOnGvisor()); - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int enable = -1; - socklen_t enableLen = sizeof(enable); - - int want = 1; - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_OOBINLINE, &enable, - &enableLen), - SyscallSucceeds()); - ASSERT_EQ(enableLen, sizeof(enable)); - EXPECT_EQ(enable, want); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc deleted file mode 100644 index 597b5bcb1..000000000 --- a/test/syscalls/linux/socket_inet_loopback.cc +++ /dev/null @@ -1,2934 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <string.h> -#include <sys/socket.h> - -#include <atomic> -#include <iostream> -#include <memory> -#include <string> -#include <tuple> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::Gt; - -PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) { - switch (family) { - case AF_INET: - return static_cast<uint16_t>( - reinterpret_cast<sockaddr_in const*>(&addr)->sin_port); - case AF_INET6: - return static_cast<uint16_t>( - reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port); - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } -} - -PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) { - switch (family) { - case AF_INET: - reinterpret_cast<sockaddr_in*>(addr)->sin_port = port; - return NoError(); - case AF_INET6: - reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port; - return NoError(); - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } -} - -struct TestParam { - TestAddress listener; - TestAddress connector; -}; - -std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) { - return absl::StrCat("Listen", info.param.listener.description, "_Connect", - info.param.connector.description); -} - -using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>; - -TEST(BadSocketPairArgs, ValidateErrForBadCallsToSocketPair) { - int fd[2] = {}; - - // Valid AF but invalid for socketpair(2) return ESOCKTNOSUPPORT. - ASSERT_THAT(socketpair(AF_INET, 0, 0, fd), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - ASSERT_THAT(socketpair(AF_INET6, 0, 0, fd), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - - // Invalid AF will fail. - ASSERT_THAT(socketpair(AF_MAX, 0, 0, fd), SyscallFails()); - ASSERT_THAT(socketpair(8675309, 0, 0, fd), SyscallFails()); -} - -enum class Operation { - Bind, - Connect, - SendTo, -}; - -std::string OperationToString(Operation operation) { - switch (operation) { - case Operation::Bind: - return "Bind"; - case Operation::Connect: - return "Connect"; - // Operation::SendTo is the default. - default: - return "SendTo"; - } -} - -using OperationSequence = std::vector<Operation>; - -using DualStackSocketTest = - ::testing::TestWithParam<std::tuple<TestAddress, OperationSequence>>; - -TEST_P(DualStackSocketTest, AddressOperations) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_DGRAM, 0)); - - const TestAddress& addr = std::get<0>(GetParam()); - const OperationSequence& operations = std::get<1>(GetParam()); - - auto addr_in = reinterpret_cast<const sockaddr*>(&addr.addr); - - // sockets may only be bound once. Both `connect` and `sendto` cause a socket - // to be bound. - bool bound = false; - for (const Operation& operation : operations) { - bool sockname = false; - bool peername = false; - switch (operation) { - case Operation::Bind: { - ASSERT_NO_ERRNO(SetAddrPort( - addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 0)); - - int bind_ret = bind(fd.get(), addr_in, addr.addr_len); - - // Dual stack sockets may only be bound to AF_INET6. - if (!bound && addr.family() == AF_INET6) { - EXPECT_THAT(bind_ret, SyscallSucceeds()); - bound = true; - - sockname = true; - } else { - EXPECT_THAT(bind_ret, SyscallFailsWithErrno(EINVAL)); - } - break; - } - case Operation::Connect: { - ASSERT_NO_ERRNO(SetAddrPort( - addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 1337)); - - EXPECT_THAT(RetryEINTR(connect)(fd.get(), addr_in, addr.addr_len), - SyscallSucceeds()) - << GetAddrStr(addr_in); - bound = true; - - sockname = true; - peername = true; - - break; - } - case Operation::SendTo: { - const char payload[] = "hello"; - ASSERT_NO_ERRNO(SetAddrPort( - addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 1337)); - - ssize_t sendto_ret = sendto(fd.get(), &payload, sizeof(payload), 0, - addr_in, addr.addr_len); - - EXPECT_THAT(sendto_ret, SyscallSucceedsWithValue(sizeof(payload))); - sockname = !bound; - bound = true; - break; - } - } - - if (sockname) { - sockaddr_storage sock_addr; - socklen_t addrlen = sizeof(sock_addr); - ASSERT_THAT(getsockname(fd.get(), reinterpret_cast<sockaddr*>(&sock_addr), - &addrlen), - SyscallSucceeds()); - ASSERT_EQ(addrlen, sizeof(struct sockaddr_in6)); - - auto sock_addr_in6 = reinterpret_cast<const sockaddr_in6*>(&sock_addr); - - if (operation == Operation::SendTo) { - EXPECT_EQ(sock_addr_in6->sin6_family, AF_INET6); - EXPECT_TRUE(IN6_IS_ADDR_UNSPECIFIED(sock_addr_in6->sin6_addr.s6_addr32)) - << OperationToString(operation) << " getsocknam=" - << GetAddrStr(reinterpret_cast<sockaddr*>(&sock_addr)); - - EXPECT_NE(sock_addr_in6->sin6_port, 0); - } else if (IN6_IS_ADDR_V4MAPPED( - reinterpret_cast<const sockaddr_in6*>(addr_in) - ->sin6_addr.s6_addr32)) { - EXPECT_TRUE(IN6_IS_ADDR_V4MAPPED(sock_addr_in6->sin6_addr.s6_addr32)) - << OperationToString(operation) << " getsocknam=" - << GetAddrStr(reinterpret_cast<sockaddr*>(&sock_addr)); - } - } - - if (peername) { - sockaddr_storage peer_addr; - socklen_t addrlen = sizeof(peer_addr); - ASSERT_THAT(getpeername(fd.get(), reinterpret_cast<sockaddr*>(&peer_addr), - &addrlen), - SyscallSucceeds()); - ASSERT_EQ(addrlen, sizeof(struct sockaddr_in6)); - - if (addr.family() == AF_INET || - IN6_IS_ADDR_V4MAPPED(reinterpret_cast<const sockaddr_in6*>(addr_in) - ->sin6_addr.s6_addr32)) { - EXPECT_TRUE(IN6_IS_ADDR_V4MAPPED( - reinterpret_cast<const sockaddr_in6*>(&peer_addr) - ->sin6_addr.s6_addr32)) - << OperationToString(operation) << " getpeername=" - << GetAddrStr(reinterpret_cast<sockaddr*>(&peer_addr)); - } - } - } -} - -// TODO(gvisor.dev/issue/1556): uncomment V4MappedAny. -INSTANTIATE_TEST_SUITE_P( - All, DualStackSocketTest, - ::testing::Combine( - ::testing::Values(V4Any(), V4Loopback(), /*V4MappedAny(),*/ - V4MappedLoopback(), V6Any(), V6Loopback()), - ::testing::ValuesIn<OperationSequence>( - {{Operation::Bind, Operation::Connect, Operation::SendTo}, - {Operation::Bind, Operation::SendTo, Operation::Connect}, - {Operation::Connect, Operation::Bind, Operation::SendTo}, - {Operation::Connect, Operation::SendTo, Operation::Bind}, - {Operation::SendTo, Operation::Bind, Operation::Connect}, - {Operation::SendTo, Operation::Connect, Operation::Bind}})), - [](::testing::TestParamInfo< - std::tuple<TestAddress, OperationSequence>> const& info) { - const TestAddress& addr = std::get<0>(info.param); - const OperationSequence& operations = std::get<1>(info.param); - std::string s = addr.description; - for (const Operation& operation : operations) { - absl::StrAppend(&s, OperationToString(operation)); - } - return s; - }); - -void tcpSimpleConnectTest(TestAddress const& listener, - TestAddress const& connector, bool unbound) { - // 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; - if (!unbound) { - 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()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - // Connect to the listening socket. - const 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()); - - // Accept the connection. - // - // We have to assign a name to the accepted socket, as unamed temporary - // objects are destructed upon full evaluation of the expression it is in, - // potentially causing the connecting socket to fail to shutdown properly. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - - ASSERT_THAT(shutdown(listen_fd.get(), SHUT_RDWR), SyscallSucceeds()); - - ASSERT_THAT(shutdown(conn_fd.get(), SHUT_RDWR), SyscallSucceeds()); -} - -TEST_P(SocketInetLoopbackTest, TCP) { - auto const& param = GetParam(); - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - - tcpSimpleConnectTest(listener, connector, true); -} - -TEST_P(SocketInetLoopbackTest, TCPListenUnbound) { - auto const& param = GetParam(); - - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - - tcpSimpleConnectTest(listener, connector, false); -} - -TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) { - const auto& param = GetParam(); - - const TestAddress& listener = param.listener; - const TestAddress& connector = param.connector; - - constexpr int kBacklog = 5; - - // Create the listening socket. - 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(), kBacklog), SyscallSucceeds()); - ASSERT_THAT(shutdown(listen_fd.get(), SHUT_RD), SyscallSucceeds()); - ASSERT_THAT(listen(listen_fd.get(), kBacklog), 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)); - - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - - // TODO(b/157236388): Remove Disable save after bug is fixed. S/R test can - // fail because the last socket may not be delivered to the accept queue - // by the time connect returns. - DisableSave ds; - for (int i = 0; i < kBacklog; i++) { - auto client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(RetryEINTR(connect)(client.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceeds()); - } - for (int i = 0; i < kBacklog; i++) { - ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), SyscallSucceeds()); - } -} - -TEST_P(SocketInetLoopbackTest, TCPListenShutdown) { - auto const& param = GetParam(); - - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - - constexpr int kBacklog = 2; - constexpr int kFDs = kBacklog + 1; - - // Create the listening socket. - 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(), kBacklog), 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()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - - // Shutdown the write of the listener, expect to not have any effect. - ASSERT_THAT(shutdown(listen_fd.get(), SHUT_WR), SyscallSucceeds()); - - for (int i = 0; i < kFDs; i++) { - auto client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(RetryEINTR(connect)(client.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceeds()); - ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), SyscallSucceeds()); - } - - // Shutdown the read of the listener, expect to fail subsequent - // server accepts, binds and client connects. - ASSERT_THAT(shutdown(listen_fd.get(), SHUT_RD), SyscallSucceeds()); - - ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), - SyscallFailsWithErrno(EINVAL)); - - // Check that shutdown did not release the port. - FileDescriptor new_listen_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT( - bind(new_listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), - listener.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Check that subsequent connection attempts receive a RST. - auto client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - - for (int i = 0; i < kFDs; i++) { - auto client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(RetryEINTR(connect)(client.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallFailsWithErrno(ECONNREFUSED)); - } -} - -TEST_P(SocketInetLoopbackTest, TCPListenClose) { - auto const& param = GetParam(); - - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - - constexpr int kAcceptCount = 2; - constexpr int kBacklog = kAcceptCount + 2; - constexpr int kFDs = kBacklog * 3; - - // Create the listening socket. - 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(), kBacklog), 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()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - std::vector<FileDescriptor> clients; - for (int i = 0; i < kFDs; i++) { - auto client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - int ret = connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len); - if (ret != 0) { - EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS)); - } - clients.push_back(std::move(client)); - } - for (int i = 0; i < kAcceptCount; i++) { - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - } -} - -void TestListenWhileConnect(const TestParam& param, - void (*stopListen)(FileDescriptor&)) { - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - - constexpr int kBacklog = 2; - // Linux completes one more connection than the listen backlog argument. - // To ensure that there is at least one client connection that stays in - // connecting state, keep 2 more client connections than the listen backlog. - // gVisor differs in this behavior though, gvisor.dev/issue/3153. - constexpr int kClients = kBacklog + 2; - - // Create the listening socket. - 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(), kBacklog), 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()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - std::vector<FileDescriptor> clients; - for (int i = 0; i < kClients; i++) { - FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - int ret = connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len); - if (ret != 0) { - EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS)); - clients.push_back(std::move(client)); - } - } - - stopListen(listen_fd); - - for (auto& client : clients) { - constexpr int kTimeout = 10000; - pollfd pfd = { - .fd = client.get(), - .events = POLLIN, - }; - // When the listening socket is closed, then we expect the remote to reset - // the connection. - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - ASSERT_EQ(pfd.revents, POLLIN | POLLHUP | POLLERR); - char c; - // Subsequent read can fail with: - // ECONNRESET: If the client connection was established and was reset by the - // remote. - // ECONNREFUSED: If the client connection failed to be established. - ASSERT_THAT(read(client.get(), &c, sizeof(c)), - AnyOf(SyscallFailsWithErrno(ECONNRESET), - SyscallFailsWithErrno(ECONNREFUSED))); - // The last client connection would be in connecting (SYN_SENT) state. - if (client.get() == clients[kClients - 1].get()) { - ASSERT_EQ(errno, ECONNREFUSED) << strerror(errno); - } - } -} - -TEST_P(SocketInetLoopbackTest, TCPListenCloseWhileConnect) { - TestListenWhileConnect(GetParam(), [](FileDescriptor& f) { - ASSERT_THAT(close(f.release()), SyscallSucceeds()); - }); -} - -TEST_P(SocketInetLoopbackTest, TCPListenShutdownWhileConnect) { - TestListenWhileConnect(GetParam(), [](FileDescriptor& f) { - ASSERT_THAT(shutdown(f.get(), SHUT_RD), SyscallSucceeds()); - }); -} - -// TODO(b/157236388): Remove _NoRandomSave once bug is fixed. Test fails w/ -// random save as established connections which can't be delivered to the accept -// queue because the queue is full are not correctly delivered after restore -// causing the last accept to timeout on the restore. -TEST_P(SocketInetLoopbackTest, TCPbacklog_NoRandomSave) { - 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()); - constexpr int kBacklogSize = 2; - ASSERT_THAT(listen(listen_fd.get(), kBacklogSize), 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()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - int i = 0; - while (1) { - int ret; - - // Connect to the listening socket. - const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - ret = connect(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len); - if (ret != 0) { - EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS)); - pollfd pfd = { - .fd = conn_fd.get(), - .events = POLLOUT, - }; - ret = poll(&pfd, 1, 3000); - if (ret == 0) break; - EXPECT_THAT(ret, SyscallSucceedsWithValue(1)); - } - EXPECT_THAT(RetryEINTR(send)(conn_fd.get(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - ASSERT_THAT(shutdown(conn_fd.get(), SHUT_RDWR), SyscallSucceeds()); - i++; - } - - for (; i != 0; i--) { - // Accept the connection. - // - // We have to assign a name to the accepted socket, as unamed temporary - // objects are destructed upon full evaluation of the expression it is in, - // potentially causing the connecting socket to fail to shutdown properly. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - } -} - -// Test if the stack completes atmost listen backlog number of client -// connections. It exercises the path of the stack that enqueues completed -// connections to accept queue vs new incoming SYNs. -TEST_P(SocketInetLoopbackTest, TCPConnectBacklog_NoRandomSave) { - const auto& param = GetParam(); - const TestAddress& listener = param.listener; - const TestAddress& connector = param.connector; - - constexpr int kBacklog = 1; - // Keep the number of client connections more than the listen backlog. - // Linux completes one more connection than the listen backlog argument. - // gVisor differs in this behavior though, gvisor.dev/issue/3153. - int kClients = kBacklog + 2; - if (IsRunningOnGvisor()) { - kClients--; - } - - // Run the following test for few iterations to test race between accept queue - // getting filled with incoming SYNs. - for (int num = 0; num < 10; num++) { - 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(), kBacklog), SyscallSucceeds()); - - socklen_t addrlen = listener.addr_len; - ASSERT_THAT( - getsockname(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), - &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - - std::vector<FileDescriptor> clients; - // Issue multiple non-blocking client connects. - for (int i = 0; i < kClients; i++) { - FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - int ret = connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len); - if (ret != 0) { - EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS)); - } - clients.push_back(std::move(client)); - } - - // Now that client connects are issued, wait for the accept queue to get - // filled and ensure no new client connection is completed. - for (int i = 0; i < kClients; i++) { - pollfd pfd = { - .fd = clients[i].get(), - .events = POLLOUT, - }; - if (i < kClients - 1) { - // Poll for client side connection completions with a large timeout. - // We cannot poll on the listener side without calling accept as poll - // stays level triggered with non-zero accept queue length. - // - // Client side poll would not guarantee that the completed connection - // has been enqueued in to the acccept queue, but the fact that the - // listener ACKd the SYN, means that it cannot complete any new incoming - // SYNs when it has already ACKd for > backlog number of SYNs. - ASSERT_THAT(poll(&pfd, 1, 10000), SyscallSucceedsWithValue(1)) - << "num=" << num << " i=" << i << " kClients=" << kClients; - ASSERT_EQ(pfd.revents, POLLOUT) << "num=" << num << " i=" << i; - } else { - // Now that we expect accept queue filled up, ensure that the last - // client connection never completes with a smaller poll timeout. - ASSERT_THAT(poll(&pfd, 1, 1000), SyscallSucceedsWithValue(0)) - << "num=" << num << " i=" << i; - } - - ASSERT_THAT(close(clients[i].release()), SyscallSucceedsWithValue(0)) - << "num=" << num << " i=" << i; - } - clients.clear(); - // We close the listening side and open a new listener. We could instead - // drain the accept queue by calling accept() and reuse the listener, but - // that is racy as the retransmitted SYNs could get ACKd as we make room in - // the accept queue. - ASSERT_THAT(close(listen_fd.release()), SyscallSucceedsWithValue(0)); - } -} - -// TCPFinWait2Test creates a pair of connected sockets then closes one end to -// trigger FIN_WAIT2 state for the closed endpoint. Then it binds the same local -// IP/port on a new socket and tries to connect. The connect should fail w/ -// an EADDRINUSE. Then we wait till the FIN_WAIT2 timeout is over and try the -// connect again with a new socket and this time it should succeed. -// -// TCP timers are not S/R today, this can cause this test to be flaky when run -// under random S/R due to timer being reset on a restore. -TEST_P(SocketInetLoopbackTest, TCPFinWait2Test_NoRandomSave) { - 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()); - - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - // Connect to the listening socket. - FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - - // Lower FIN_WAIT2 state to 5 seconds for test. - constexpr int kTCPLingerTimeout = 5; - EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2, - &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)), - SyscallSucceedsWithValue(0)); - - 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()); - - // Accept the connection. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - - // Get the address/port bound by the connecting socket. - sockaddr_storage conn_bound_addr; - socklen_t conn_addrlen = connector.addr_len; - ASSERT_THAT( - getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr), - &conn_addrlen), - SyscallSucceeds()); - - // close the connecting FD to trigger FIN_WAIT2 on the connected fd. - conn_fd.reset(); - - // Now bind and connect a new socket. - const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - - // Disable cooperative saves after this point. As a save between the first - // bind/connect and the second one can cause the linger timeout timer to - // be restarted causing the final bind/connect to fail. - DisableSave ds; - - ASSERT_THAT(bind(conn_fd2.get(), - reinterpret_cast<sockaddr*>(&conn_bound_addr), conn_addrlen), - SyscallFailsWithErrno(EADDRINUSE)); - - // Sleep for a little over the linger timeout to reduce flakiness in - // save/restore tests. - absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2)); - - ds.reset(); - - ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - conn_addrlen), - SyscallSucceeds()); -} - -// TCPLinger2TimeoutAfterClose creates a pair of connected sockets -// then closes one end to trigger FIN_WAIT2 state for the closed endpont. -// It then sleeps for the TCP_LINGER2 timeout and verifies that bind/ -// connecting the same address succeeds. -// -// TCP timers are not S/R today, this can cause this test to be flaky when run -// under random S/R due to timer being reset on a restore. -TEST_P(SocketInetLoopbackTest, TCPLinger2TimeoutAfterClose_NoRandomSave) { - 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()); - - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - // 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()); - - // Accept the connection. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - - // Get the address/port bound by the connecting socket. - sockaddr_storage conn_bound_addr; - socklen_t conn_addrlen = connector.addr_len; - ASSERT_THAT( - getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr), - &conn_addrlen), - SyscallSucceeds()); - - // Disable cooperative saves after this point as TCP timers are not restored - // across a S/R. - { - DisableSave ds; - constexpr int kTCPLingerTimeout = 5; - EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2, - &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)), - SyscallSucceedsWithValue(0)); - - // close the connecting FD to trigger FIN_WAIT2 on the connected fd. - conn_fd.reset(); - - absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1)); - - // ds going out of scope will Re-enable S/R's since at this point the timer - // must have fired and cleaned up the endpoint. - } - - // Now bind and connect a new socket and verify that we can immediately - // rebind the address bound by the conn_fd as it never entered TIME_WAIT. - const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT(bind(conn_fd2.get(), - reinterpret_cast<sockaddr*>(&conn_bound_addr), conn_addrlen), - SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(connect)(conn_fd2.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - conn_addrlen), - SyscallSucceeds()); -} - -// TCPResetAfterClose creates a pair of connected sockets then closes -// one end to trigger FIN_WAIT2 state for the closed endpoint verifies -// that we generate RSTs for any new data after the socket is fully -// closed. -TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) { - 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()); - - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - // 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()); - - // Accept the connection. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - - // close the connecting FD to trigger FIN_WAIT2 on the connected fd. - conn_fd.reset(); - - int data = 1234; - - // Now send data which should trigger a RST as the other end should - // have timed out and closed the socket. - EXPECT_THAT(RetryEINTR(send)(accepted.get(), &data, sizeof(data), 0), - SyscallSucceeds()); - // Sleep for a shortwhile to get a RST back. - absl::SleepFor(absl::Seconds(1)); - - // Try writing again and we should get an EPIPE back. - EXPECT_THAT(RetryEINTR(send)(accepted.get(), &data, sizeof(data), 0), - SyscallFailsWithErrno(EPIPE)); - - // Trying to read should return zero as the other end did send - // us a FIN. We do it twice to verify that the RST does not cause an - // ECONNRESET on the read after EOF has been read by applicaiton. - EXPECT_THAT(RetryEINTR(recv)(accepted.get(), &data, sizeof(data), 0), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(RetryEINTR(recv)(accepted.get(), &data, sizeof(data), 0), - SyscallSucceedsWithValue(0)); -} - -// setupTimeWaitClose sets up a socket endpoint in TIME_WAIT state. -// Callers can choose to perform active close on either ends of the connection -// and also specify if they want to enabled SO_REUSEADDR. -void setupTimeWaitClose(const TestAddress* listener, - const TestAddress* connector, bool reuse, - bool accept_close, sockaddr_storage* listen_addr, - sockaddr_storage* conn_bound_addr) { - // Create the listening socket. - FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(listener->family(), SOCK_STREAM, IPPROTO_TCP)); - if (reuse) { - ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - } - 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()); - - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener->family(), *listen_addr)); - - // Connect to the listening socket. - FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector->family(), SOCK_STREAM, IPPROTO_TCP)); - - // We disable saves after this point as a S/R causes the netstack seed - // to be regenerated which changes what ports/ISN is picked for a given - // tuple (src ip,src port, dst ip, dst port). This can cause the final - // SYN to use a sequence number that looks like one from the current - // connection in TIME_WAIT and will not be accepted causing the test - // to timeout. - // - // TODO(gvisor.dev/issue/940): S/R portSeed/portHint - DisableSave ds; - - 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()); - - // Accept the connection. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - - // Get the address/port bound by the connecting socket. - socklen_t conn_addrlen = connector->addr_len; - ASSERT_THAT( - getsockname(conn_fd.get(), reinterpret_cast<sockaddr*>(conn_bound_addr), - &conn_addrlen), - SyscallSucceeds()); - - FileDescriptor active_closefd, passive_closefd; - if (accept_close) { - active_closefd = std::move(accepted); - passive_closefd = std::move(conn_fd); - } else { - active_closefd = std::move(conn_fd); - passive_closefd = std::move(accepted); - } - - // shutdown to trigger TIME_WAIT. - ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds()); - { - constexpr int kTimeout = 10000; - pollfd pfd = { - .fd = passive_closefd.get(), - .events = POLLIN, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - ASSERT_EQ(pfd.revents, POLLIN); - } - ASSERT_THAT(shutdown(passive_closefd.get(), SHUT_WR), SyscallSucceeds()); - { - constexpr int kTimeout = 10000; - constexpr int16_t want_events = POLLHUP; - pollfd pfd = { - .fd = active_closefd.get(), - .events = want_events, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - } - - // This sleep is needed to reduce flake to ensure that the passive-close - // ensures the state transitions to CLOSE from LAST_ACK. - absl::SleepFor(absl::Seconds(1)); -} - -// These tests are disabled under random save as the the restore run -// results in the stack.Seed() being different which can cause -// sequence number of final connect to be one that is considered -// old and can cause the test to be flaky. -// -// Test re-binding of client and server bound addresses when the older -// connection is in TIME_WAIT. -TEST_P(SocketInetLoopbackTest, TCPPassiveCloseNoTimeWaitTest_NoRandomSave) { - auto const& param = GetParam(); - sockaddr_storage listen_addr, conn_bound_addr; - listen_addr = param.listener.addr; - setupTimeWaitClose(¶m.listener, ¶m.connector, false /*reuse*/, - true /*accept_close*/, &listen_addr, &conn_bound_addr); - - // Now bind a new socket and verify that we can immediately rebind the address - // bound by the conn_fd as it never entered TIME_WAIT. - const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(bind(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr), - param.connector.addr_len), - SyscallSucceeds()); - - FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), - param.listener.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(SocketInetLoopbackTest, - TCPPassiveCloseNoTimeWaitReuseTest_NoRandomSave) { - auto const& param = GetParam(); - sockaddr_storage listen_addr, conn_bound_addr; - listen_addr = param.listener.addr; - setupTimeWaitClose(¶m.listener, ¶m.connector, true /*reuse*/, - true /*accept_close*/, &listen_addr, &conn_bound_addr); - - FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), - param.listener.addr_len), - SyscallSucceeds()); - ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds()); - - // Now bind and connect new socket and verify that we can immediately rebind - // the address bound by the conn_fd as it never entered TIME_WAIT. - const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr), - param.connector.addr_len), - SyscallSucceeds()); - - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(param.listener.family(), listen_addr)); - sockaddr_storage conn_addr = param.connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(param.connector.family(), &conn_addr, port)); - ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - param.connector.addr_len), - SyscallSucceeds()); -} - -TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitTest_NoRandomSave) { - auto const& param = GetParam(); - sockaddr_storage listen_addr, conn_bound_addr; - listen_addr = param.listener.addr; - setupTimeWaitClose(¶m.listener, ¶m.connector, false /*reuse*/, - false /*accept_close*/, &listen_addr, &conn_bound_addr); - FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT(bind(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr), - param.connector.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitReuseTest_NoRandomSave) { - auto const& param = GetParam(); - sockaddr_storage listen_addr, conn_bound_addr; - listen_addr = param.listener.addr; - setupTimeWaitClose(¶m.listener, ¶m.connector, true /*reuse*/, - false /*accept_close*/, &listen_addr, &conn_bound_addr); - FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_bound_addr), - param.connector.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) { - 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 userTimeout on the listening socket. - constexpr int kUserTimeout = 10; - ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &kUserTimeout, sizeof(kUserTimeout)), - 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()); - - // Accept the connection. - auto accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - // Verify that the accepted socket inherited the user timeout set on - // listening socket. - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(accepted.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kUserTimeout); -} - -TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) { - 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)); - - // 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)); - - // TODO(b/157236388): Reenable Cooperative S/R once bug is fixed. - DisableSave ds; - ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceeds()); - - // Trigger a RST by turning linger off and closing the socket. - struct linger opt = { - .l_onoff = 1, - .l_linger = 0, - }; - ASSERT_THAT( - setsockopt(conn_fd.get(), SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)), - SyscallSucceeds()); - ASSERT_THAT(close(conn_fd.release()), SyscallSucceeds()); - - if (IsRunningOnGvisor()) { - // Gvisor packet procssing is asynchronous and can take a bit of time in - // some cases so we give it a bit of time to process the RST packet before - // calling accept. - // - // There is nothing to poll() on so we have no choice but to use a sleep - // here. - absl::SleepFor(absl::Milliseconds(100)); - } - - sockaddr_storage accept_addr; - socklen_t addrlen = sizeof(accept_addr); - - auto accept_fd = ASSERT_NO_ERRNO_AND_VALUE(Accept( - listen_fd.get(), reinterpret_cast<sockaddr*>(&accept_addr), &addrlen)); - ASSERT_EQ(addrlen, listener.addr_len); - - // Wait for accept_fd to process the RST. - constexpr int kTimeout = 10000; - pollfd pfd = { - .fd = accept_fd.get(), - .events = POLLIN, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - ASSERT_EQ(pfd.revents, POLLIN | POLLHUP | POLLERR); - - { - int err; - socklen_t optlen = sizeof(err); - ASSERT_THAT( - getsockopt(accept_fd.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), - SyscallSucceeds()); - // This should return ECONNRESET as the socket just received a RST packet - // from the peer. - ASSERT_EQ(optlen, sizeof(err)); - ASSERT_EQ(err, ECONNRESET); - } - { - int err; - socklen_t optlen = sizeof(err); - ASSERT_THAT( - getsockopt(accept_fd.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), - SyscallSucceeds()); - // This should return no error as the previous getsockopt call would have - // cleared the socket error. - ASSERT_EQ(optlen, sizeof(err)); - ASSERT_EQ(err, 0); - } - { - sockaddr_storage peer_addr; - socklen_t addrlen = sizeof(peer_addr); - // The socket is not connected anymore and should return ENOTCONN. - ASSERT_THAT(getpeername(accept_fd.get(), - reinterpret_cast<sockaddr*>(&peer_addr), &addrlen), - SyscallFailsWithErrno(ENOTCONN)); - } -} - -// 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( - // Listeners bound to IPv4 addresses refuse connections using IPv6 - // addresses. - TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()}, - TestParam{V4Any(), V4MappedAny()}, - TestParam{V4Any(), V4MappedLoopback()}, - TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()}, - TestParam{V4Loopback(), V4MappedLoopback()}, - TestParam{V4MappedAny(), V4Any()}, - TestParam{V4MappedAny(), V4Loopback()}, - TestParam{V4MappedAny(), V4MappedAny()}, - TestParam{V4MappedAny(), V4MappedLoopback()}, - TestParam{V4MappedLoopback(), V4Any()}, - TestParam{V4MappedLoopback(), V4Loopback()}, - TestParam{V4MappedLoopback(), V4MappedLoopback()}, - - // Listeners bound to IN6ADDR_ANY accept all connections. - TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()}, - TestParam{V6Any(), V4MappedAny()}, - TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()}, - TestParam{V6Any(), V6Loopback()}, - - // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4 - // addresses. - TestParam{V6Loopback(), V6Any()}, - TestParam{V6Loopback(), V6Loopback()}), - DescribeTestParam); - -using SocketInetReusePortTest = ::testing::TestWithParam<TestParam>; - -// TODO(gvisor.dev/issue/940): Remove _NoRandomSave when portHint/stack.Seed is -// saved/restored. -TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread_NoRandomSave) { - auto const& param = GetParam(); - - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - sockaddr_storage listen_addr = listener.addr; - sockaddr_storage conn_addr = connector.addr; - constexpr int kThreadCount = 3; - constexpr int kConnectAttempts = 10000; - - // Create the listening socket. - FileDescriptor listener_fds[kThreadCount]; - for (int i = 0; i < kThreadCount; i++) { - listener_fds[i] = ASSERT_NO_ERRNO_AND_VALUE( - Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP)); - int fd = listener_fds[i].get(); - - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT( - bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len), - SyscallSucceeds()); - ASSERT_THAT(listen(fd, 40), SyscallSucceeds()); - - // On the first bind we need to determine which port was bound. - if (i != 0) { - continue; - } - - // Get the port bound by the listening socket. - socklen_t addrlen = listener.addr_len; - ASSERT_THAT( - getsockname(listener_fds[0].get(), - reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port)); - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - } - - std::atomic<int> connects_received = ATOMIC_VAR_INIT(0); - std::unique_ptr<ScopedThread> listen_thread[kThreadCount]; - int accept_counts[kThreadCount] = {}; - // TODO(avagin): figure how to not disable S/R for the whole test. - // We need to take into account that this test executes a lot of system - // calls from many threads. - DisableSave ds; - - for (int i = 0; i < kThreadCount; i++) { - listen_thread[i] = absl::make_unique<ScopedThread>( - [&listener_fds, &accept_counts, i, &connects_received]() { - do { - auto fd = Accept(listener_fds[i].get(), nullptr, nullptr); - if (!fd.ok()) { - if (connects_received >= kConnectAttempts) { - // Another thread have shutdown our read side causing the - // accept to fail. - ASSERT_EQ(errno, EINVAL); - break; - } - ASSERT_NO_ERRNO(fd); - break; - } - // Receive some data from a socket to be sure that the connect() - // system call has been completed on another side. - // Do a short read and then close the socket to trigger a RST. This - // ensures that both ends of the connection are cleaned up and no - // goroutines hang around in TIME-WAIT. We do this so that this test - // does not timeout under gotsan runs where lots of goroutines can - // cause the test to use absurd amounts of memory. - // - // See: https://tools.ietf.org/html/rfc2525#page-50 section 2.17 - uint16_t data; - EXPECT_THAT( - RetryEINTR(recv)(fd.ValueOrDie().get(), &data, sizeof(data), 0), - SyscallSucceedsWithValue(sizeof(data))); - accept_counts[i]++; - } while (++connects_received < kConnectAttempts); - - // Shutdown all sockets to wake up other threads. - for (int j = 0; j < kThreadCount; j++) { - shutdown(listener_fds[j].get(), SHUT_RDWR); - } - }); - } - - ScopedThread connecting_thread([&connector, &conn_addr]() { - for (int32_t i = 0; i < kConnectAttempts; i++) { - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT( - RetryEINTR(connect)(fd.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceeds()); - - EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0), - SyscallSucceedsWithValue(sizeof(i))); - } - }); - - // Join threads to be sure that all connections have been counted - connecting_thread.Join(); - for (int i = 0; i < kThreadCount; i++) { - listen_thread[i]->Join(); - } - // Check that connections are distributed fairly between listening sockets - for (int i = 0; i < kThreadCount; i++) - EXPECT_THAT(accept_counts[i], - EquivalentWithin((kConnectAttempts / kThreadCount), 0.10)); -} - -TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread_NoRandomSave) { - auto const& param = GetParam(); - - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - sockaddr_storage listen_addr = listener.addr; - sockaddr_storage conn_addr = connector.addr; - constexpr int kThreadCount = 3; - - // Create the listening socket. - FileDescriptor listener_fds[kThreadCount]; - for (int i = 0; i < kThreadCount; i++) { - listener_fds[i] = - ASSERT_NO_ERRNO_AND_VALUE(Socket(listener.family(), SOCK_DGRAM, 0)); - int fd = listener_fds[i].get(); - - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT( - bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len), - SyscallSucceeds()); - - // On the first bind we need to determine which port was bound. - if (i != 0) { - continue; - } - - // Get the port bound by the listening socket. - socklen_t addrlen = listener.addr_len; - ASSERT_THAT( - getsockname(listener_fds[0].get(), - reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port)); - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - } - - constexpr int kConnectAttempts = 10000; - std::atomic<int> packets_received = ATOMIC_VAR_INIT(0); - std::unique_ptr<ScopedThread> receiver_thread[kThreadCount]; - int packets_per_socket[kThreadCount] = {}; - // TODO(avagin): figure how to not disable S/R for the whole test. - DisableSave ds; // Too expensive. - - for (int i = 0; i < kThreadCount; i++) { - receiver_thread[i] = absl::make_unique<ScopedThread>( - [&listener_fds, &packets_per_socket, i, &packets_received]() { - do { - struct sockaddr_storage addr = {}; - socklen_t addrlen = sizeof(addr); - int data; - - auto ret = RetryEINTR(recvfrom)( - listener_fds[i].get(), &data, sizeof(data), 0, - reinterpret_cast<struct sockaddr*>(&addr), &addrlen); - - if (packets_received < kConnectAttempts) { - ASSERT_THAT(ret, SyscallSucceedsWithValue(sizeof(data))); - } - - if (ret != sizeof(data)) { - // Another thread may have shutdown our read side causing the - // recvfrom to fail. - break; - } - - packets_received++; - packets_per_socket[i]++; - - // A response is required to synchronize with the main thread, - // otherwise the main thread can send more than can fit into receive - // queues. - EXPECT_THAT(RetryEINTR(sendto)( - listener_fds[i].get(), &data, sizeof(data), 0, - reinterpret_cast<sockaddr*>(&addr), addrlen), - SyscallSucceedsWithValue(sizeof(data))); - } while (packets_received < kConnectAttempts); - - // Shutdown all sockets to wake up other threads. - for (int j = 0; j < kThreadCount; j++) - shutdown(listener_fds[j].get(), SHUT_RDWR); - }); - } - - ScopedThread main_thread([&connector, &conn_addr]() { - for (int i = 0; i < kConnectAttempts; i++) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(connector.family(), SOCK_DGRAM, 0)); - EXPECT_THAT(RetryEINTR(sendto)(fd.get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceedsWithValue(sizeof(i))); - int data; - EXPECT_THAT(RetryEINTR(recv)(fd.get(), &data, sizeof(data), 0), - SyscallSucceedsWithValue(sizeof(data))); - } - }); - - main_thread.Join(); - - // Join threads to be sure that all connections have been counted - for (int i = 0; i < kThreadCount; i++) { - receiver_thread[i]->Join(); - } - // Check that packets are distributed fairly between listening sockets. - for (int i = 0; i < kThreadCount; i++) - EXPECT_THAT(packets_per_socket[i], - EquivalentWithin((kConnectAttempts / kThreadCount), 0.10)); -} - -TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThreadShort_NoRandomSave) { - auto const& param = GetParam(); - - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - sockaddr_storage listen_addr = listener.addr; - sockaddr_storage conn_addr = connector.addr; - constexpr int kThreadCount = 3; - - // TODO(b/141211329): endpointsByNic.seed has to be saved/restored. - const DisableSave ds141211329; - - // Create listening sockets. - FileDescriptor listener_fds[kThreadCount]; - for (int i = 0; i < kThreadCount; i++) { - listener_fds[i] = - ASSERT_NO_ERRNO_AND_VALUE(Socket(listener.family(), SOCK_DGRAM, 0)); - int fd = listener_fds[i].get(); - - ASSERT_THAT(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT( - bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len), - SyscallSucceeds()); - - // On the first bind we need to determine which port was bound. - if (i != 0) { - continue; - } - - // Get the port bound by the listening socket. - socklen_t addrlen = listener.addr_len; - ASSERT_THAT( - getsockname(listener_fds[0].get(), - reinterpret_cast<sockaddr*>(&listen_addr), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - ASSERT_NO_ERRNO(SetAddrPort(listener.family(), &listen_addr, port)); - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - } - - constexpr int kConnectAttempts = 10; - FileDescriptor client_fds[kConnectAttempts]; - - // Do the first run without save/restore. - DisableSave ds; - for (int i = 0; i < kConnectAttempts; i++) { - client_fds[i] = - ASSERT_NO_ERRNO_AND_VALUE(Socket(connector.family(), SOCK_DGRAM, 0)); - EXPECT_THAT(RetryEINTR(sendto)(client_fds[i].get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceedsWithValue(sizeof(i))); - } - ds.reset(); - - // Check that a mapping of client and server sockets has - // not been change after save/restore. - for (int i = 0; i < kConnectAttempts; i++) { - EXPECT_THAT(RetryEINTR(sendto)(client_fds[i].get(), &i, sizeof(i), 0, - reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len), - SyscallSucceedsWithValue(sizeof(i))); - } - - pollfd pollfds[kThreadCount]; - for (int i = 0; i < kThreadCount; i++) { - pollfds[i].fd = listener_fds[i].get(); - pollfds[i].events = POLLIN; - } - - std::map<uint16_t, int> portToFD; - - int received = 0; - while (received < kConnectAttempts * 2) { - ASSERT_THAT(poll(pollfds, kThreadCount, -1), - SyscallSucceedsWithValue(Gt(0))); - - for (int i = 0; i < kThreadCount; i++) { - if ((pollfds[i].revents & POLLIN) == 0) { - continue; - } - - received++; - - const int fd = pollfds[i].fd; - struct sockaddr_storage addr = {}; - socklen_t addrlen = sizeof(addr); - int data; - EXPECT_THAT(RetryEINTR(recvfrom)( - fd, &data, sizeof(data), 0, - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceedsWithValue(sizeof(data))); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(connector.family(), addr)); - auto prev_port = portToFD.find(port); - // Check that all packets from one client have been delivered to the - // same server socket. - if (prev_port == portToFD.end()) { - portToFD[port] = fd; - } else { - EXPECT_EQ(portToFD[port], fd); - } - } - } -} - -INSTANTIATE_TEST_SUITE_P( - All, SocketInetReusePortTest, - ::testing::Values( - // Listeners bound to IPv4 addresses refuse connections using IPv6 - // addresses. - TestParam{V4Any(), V4Loopback()}, - TestParam{V4Loopback(), V4MappedLoopback()}, - - // Listeners bound to IN6ADDR_ANY accept all connections. - TestParam{V6Any(), V4Loopback()}, TestParam{V6Any(), V6Loopback()}, - - // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4 - // addresses. - TestParam{V6Loopback(), V6Loopback()}), - DescribeTestParam); - -struct ProtocolTestParam { - std::string description; - int type; -}; - -std::string DescribeProtocolTestParam( - ::testing::TestParamInfo<ProtocolTestParam> const& info) { - return info.param.description; -} - -using SocketMultiProtocolInetLoopbackTest = - ::testing::TestWithParam<ProtocolTestParam>; - -TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) { - auto const& param = GetParam(); - - for (int i = 0; true; i++) { - // Bind the v4 loopback on a dual stack socket. - TestAddress const& test_addr_dual = V4MappedLoopback(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_dual.family(), param.type, 0)); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that we can still bind the v6 loopback on the same port. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); - const FileDescriptor fd_v6 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); - int ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len); - if (ret == -1 && errno == EADDRINUSE) { - // Port may have been in use. - ASSERT_LT(i, 100); // Give up after 100 tries. - continue; - } - ASSERT_THAT(ret, SyscallSucceeds()); - - // Verify that binding the v4 loopback with the same port on a v4 socket - // fails. - TestAddress const& test_addr_v4 = V4Loopback(); - sockaddr_storage addr_v4 = test_addr_v4.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); - const FileDescriptor fd_v4 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4), - test_addr_v4.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // No need to try again. - break; - } -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) { - auto const& param = GetParam(); - - for (int i = 0; true; i++) { - // Bind the v4 any on a dual stack socket. - TestAddress const& test_addr_dual = V4MappedAny(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_dual.family(), param.type, 0)); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that we can still bind the v6 loopback on the same port. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); - const FileDescriptor fd_v6 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); - int ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len); - if (ret == -1 && errno == EADDRINUSE) { - // Port may have been in use. - ASSERT_LT(i, 100); // Give up after 100 tries. - continue; - } - ASSERT_THAT(ret, SyscallSucceeds()); - - // Verify that binding the v4 loopback with the same port on a v4 socket - // fails. - TestAddress const& test_addr_v4 = V4Loopback(); - sockaddr_storage addr_v4 = test_addr_v4.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); - const FileDescriptor fd_v4 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4), - test_addr_v4.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // No need to try again. - break; - } -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) { - auto const& param = GetParam(); - - // Bind the v6 any on a dual stack socket. - TestAddress const& test_addr_dual = V6Any(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_dual.family(), param.type, 0)); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that binding the v6 loopback with the same port fails. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); - const FileDescriptor fd_v6 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 loopback on the same port with a v6 socket - // fails. - TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); - sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port)); - const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_mapped.family(), param.type, 0)); - ASSERT_THAT( - bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped), - test_addr_v4_mapped.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 loopback on the same port with a v4 socket - // fails. - TestAddress const& test_addr_v4 = V4Loopback(); - sockaddr_storage addr_v4 = test_addr_v4.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); - const FileDescriptor fd_v4 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4), - test_addr_v4.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 any on the same port with a v4 socket - // fails. - TestAddress const& test_addr_v4_any = V4Any(); - sockaddr_storage addr_v4_any = test_addr_v4_any.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_any.family(), &addr_v4_any, port)); - const FileDescriptor fd_v4_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_any.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v4_any.get(), reinterpret_cast<sockaddr*>(&addr_v4_any), - test_addr_v4_any.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, - DualStackV6AnyReuseAddrDoesNotReserveV4Any) { - auto const& param = GetParam(); - - // Bind the v6 any on a dual stack socket. - TestAddress const& test_addr_dual = V6Any(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_dual.family(), param.type, 0)); - ASSERT_THAT(setsockopt(fd_dual.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that binding the v4 any on the same port with a v4 socket succeeds. - TestAddress const& test_addr_v4_any = V4Any(); - sockaddr_storage addr_v4_any = test_addr_v4_any.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_any.family(), &addr_v4_any, port)); - const FileDescriptor fd_v4_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_any.family(), param.type, 0)); - ASSERT_THAT(setsockopt(fd_v4_any.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(fd_v4_any.get(), reinterpret_cast<sockaddr*>(&addr_v4_any), - test_addr_v4_any.addr_len), - SyscallSucceeds()); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, - DualStackV6AnyReuseAddrListenReservesV4Any) { - auto const& param = GetParam(); - - // Only TCP sockets are supported. - SKIP_IF((param.type & SOCK_STREAM) == 0); - - // Bind the v6 any on a dual stack socket. - TestAddress const& test_addr_dual = V6Any(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_dual.family(), param.type, 0)); - ASSERT_THAT(setsockopt(fd_dual.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - ASSERT_THAT(listen(fd_dual.get(), 5), SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that binding the v4 any on the same port with a v4 socket succeeds. - TestAddress const& test_addr_v4_any = V4Any(); - sockaddr_storage addr_v4_any = test_addr_v4_any.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_any.family(), &addr_v4_any, port)); - const FileDescriptor fd_v4_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_any.family(), param.type, 0)); - ASSERT_THAT(setsockopt(fd_v4_any.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(bind(fd_v4_any.get(), reinterpret_cast<sockaddr*>(&addr_v4_any), - test_addr_v4_any.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, - DualStackV6AnyWithListenReservesEverything) { - auto const& param = GetParam(); - - // Only TCP sockets are supported. - SKIP_IF((param.type & SOCK_STREAM) == 0); - - // Bind the v6 any on a dual stack socket. - TestAddress const& test_addr_dual = V6Any(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_dual.family(), param.type, 0)); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - ASSERT_THAT(listen(fd_dual.get(), 5), SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that binding the v6 loopback with the same port fails. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); - const FileDescriptor fd_v6 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 loopback on the same port with a v6 socket - // fails. - TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); - sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port)); - const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_mapped.family(), param.type, 0)); - ASSERT_THAT( - bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped), - test_addr_v4_mapped.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 loopback on the same port with a v4 socket - // fails. - TestAddress const& test_addr_v4 = V4Loopback(); - sockaddr_storage addr_v4 = test_addr_v4.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4.family(), &addr_v4, port)); - const FileDescriptor fd_v4 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4), - test_addr_v4.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 any on the same port with a v4 socket - // fails. - TestAddress const& test_addr_v4_any = V4Any(); - sockaddr_storage addr_v4_any = test_addr_v4_any.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_any.family(), &addr_v4_any, port)); - const FileDescriptor fd_v4_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_any.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v4_any.get(), reinterpret_cast<sockaddr*>(&addr_v4_any), - test_addr_v4_any.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) { - auto const& param = GetParam(); - - for (int i = 0; true; i++) { - // Bind the v6 any on a v6-only socket. - TestAddress const& test_addr_dual = V6Any(); - sockaddr_storage addr_dual = test_addr_dual.addr; - const FileDescriptor fd_dual = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_dual.family(), param.type, 0)); - EXPECT_THAT(setsockopt(fd_dual.get(), IPPROTO_IPV6, IPV6_V6ONLY, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(fd_dual.get(), reinterpret_cast<sockaddr*>(&addr_dual), - test_addr_dual.addr_len), - SyscallSucceeds()); - - // Get the port that we bound. - socklen_t addrlen = test_addr_dual.addr_len; - ASSERT_THAT(getsockname(fd_dual.get(), - reinterpret_cast<sockaddr*>(&addr_dual), &addrlen), - SyscallSucceeds()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr_dual.family(), addr_dual)); - - // Verify that binding the v6 loopback with the same port fails. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v6.family(), &addr_v6, port)); - const FileDescriptor fd_v6 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that we can still bind the v4 loopback on the same port. - TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); - sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, port)); - const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_mapped.family(), param.type, 0)); - int ret = - bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped), - test_addr_v4_mapped.addr_len); - if (ret == -1 && errno == EADDRINUSE) { - // Port may have been in use. - ASSERT_LT(i, 100); // Give up after 100 tries. - continue; - } - ASSERT_THAT(ret, SyscallSucceeds()); - - // No need to try again. - break; - } -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) { - auto const& param = GetParam(); - - for (int i = 0; true; i++) { - // Bind the v6 loopback on a dual stack socket. - TestAddress const& test_addr = V6Loopback(); - sockaddr_storage bound_addr = test_addr.addr; - const FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - - // Listen iff TCP. - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); - } - - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Connect to bind an ephemeral port. - const FileDescriptor connected_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), - reinterpret_cast<sockaddr*>(&bound_addr), - bound_addr_len), - SyscallSucceeds()); - - // Get the ephemeral port. - sockaddr_storage connected_addr = {}; - socklen_t connected_addr_len = sizeof(connected_addr); - ASSERT_THAT(getsockname(connected_fd.get(), - reinterpret_cast<sockaddr*>(&connected_addr), - &connected_addr_len), - SyscallSucceeds()); - uint16_t const ephemeral_port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); - - // Verify that we actually got an ephemeral port. - ASSERT_NE(ephemeral_port, 0); - - // Verify that the ephemeral port is reserved. - const FileDescriptor checking_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - EXPECT_THAT( - bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr), - connected_addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v6 loopback with the same port fails. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port)); - const FileDescriptor fd_v6 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v6.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that we can still bind the v4 loopback on the same port. - TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); - sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, - ephemeral_port)); - const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_mapped.family(), param.type, 0)); - int ret = - bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped), - test_addr_v4_mapped.addr_len); - if (ret == -1 && errno == EADDRINUSE) { - // Port may have been in use. - ASSERT_LT(i, 100); // Give up after 100 tries. - continue; - } - EXPECT_THAT(ret, SyscallSucceeds()); - - // No need to try again. - break; - } -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReservedReuseAddr) { - auto const& param = GetParam(); - - // Bind the v6 loopback on a dual stack socket. - TestAddress const& test_addr = V6Loopback(); - sockaddr_storage bound_addr = test_addr.addr; - const FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Listen iff TCP. - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); - } - - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Connect to bind an ephemeral port. - const FileDescriptor connected_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), - reinterpret_cast<sockaddr*>(&bound_addr), - bound_addr_len), - SyscallSucceeds()); - - // Get the ephemeral port. - sockaddr_storage connected_addr = {}; - socklen_t connected_addr_len = sizeof(connected_addr); - ASSERT_THAT(getsockname(connected_fd.get(), - reinterpret_cast<sockaddr*>(&connected_addr), - &connected_addr_len), - SyscallSucceeds()); - uint16_t const ephemeral_port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); - - // Verify that we actually got an ephemeral port. - ASSERT_NE(ephemeral_port, 0); - - // Verify that the ephemeral port is not reserved. - const FileDescriptor checking_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - EXPECT_THAT( - bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr), - connected_addr_len), - SyscallSucceeds()); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) { - auto const& param = GetParam(); - - for (int i = 0; true; i++) { - // Bind the v4 loopback on a dual stack socket. - TestAddress const& test_addr = V4MappedLoopback(); - sockaddr_storage bound_addr = test_addr.addr; - const FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - - // Listen iff TCP. - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); - } - - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Connect to bind an ephemeral port. - const FileDescriptor connected_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), - reinterpret_cast<sockaddr*>(&bound_addr), - bound_addr_len), - SyscallSucceeds()); - - // Get the ephemeral port. - sockaddr_storage connected_addr = {}; - socklen_t connected_addr_len = sizeof(connected_addr); - ASSERT_THAT(getsockname(connected_fd.get(), - reinterpret_cast<sockaddr*>(&connected_addr), - &connected_addr_len), - SyscallSucceeds()); - uint16_t const ephemeral_port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); - - // Verify that we actually got an ephemeral port. - ASSERT_NE(ephemeral_port, 0); - - // Verify that the ephemeral port is reserved. - const FileDescriptor checking_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - EXPECT_THAT( - bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr), - connected_addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 loopback on the same port with a v4 socket - // fails. - TestAddress const& test_addr_v4 = V4Loopback(); - sockaddr_storage addr_v4 = test_addr_v4.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v4.family(), &addr_v4, ephemeral_port)); - const FileDescriptor fd_v4 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr_v4.family(), param.type, 0)); - EXPECT_THAT(bind(fd_v4.get(), reinterpret_cast<sockaddr*>(&addr_v4), - test_addr_v4.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v6 any on the same port with a dual-stack socket - // fails. - TestAddress const& test_addr_v6_any = V6Any(); - sockaddr_storage addr_v6_any = test_addr_v6_any.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v6_any.family(), &addr_v6_any, ephemeral_port)); - const FileDescriptor fd_v6_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v6_any.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v6_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any), - test_addr_v6_any.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // For some reason, binding the TCP v6-only any is flaky on Linux. Maybe we - // tend to run out of ephemeral ports? Regardless, binding the v6 loopback - // seems pretty reliable. Only try to bind the v6-only any on UDP and - // gVisor. - - int ret = -1; - - if (!IsRunningOnGvisor() && param.type == SOCK_STREAM) { - // Verify that we can still bind the v6 loopback on the same port. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port)); - const FileDescriptor fd_v6 = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v6.family(), param.type, 0)); - ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len); - } else { - // Verify that we can still bind the v6 any on the same port with a - // v6-only socket. - const FileDescriptor fd_v6_only_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v6_any.family(), param.type, 0)); - EXPECT_THAT(setsockopt(fd_v6_only_any.get(), IPPROTO_IPV6, IPV6_V6ONLY, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ret = - bind(fd_v6_only_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any), - test_addr_v6_any.addr_len); - } - - if (ret == -1 && errno == EADDRINUSE) { - // Port may have been in use. - ASSERT_LT(i, 100); // Give up after 100 tries. - continue; - } - EXPECT_THAT(ret, SyscallSucceeds()); - - // No need to try again. - break; - } -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, - V4MappedEphemeralPortReservedResueAddr) { - auto const& param = GetParam(); - - // Bind the v4 loopback on a dual stack socket. - TestAddress const& test_addr = V4MappedLoopback(); - sockaddr_storage bound_addr = test_addr.addr; - const FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - - ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Listen iff TCP. - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); - } - - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Connect to bind an ephemeral port. - const FileDescriptor connected_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), - reinterpret_cast<sockaddr*>(&bound_addr), - bound_addr_len), - SyscallSucceeds()); - - // Get the ephemeral port. - sockaddr_storage connected_addr = {}; - socklen_t connected_addr_len = sizeof(connected_addr); - ASSERT_THAT(getsockname(connected_fd.get(), - reinterpret_cast<sockaddr*>(&connected_addr), - &connected_addr_len), - SyscallSucceeds()); - uint16_t const ephemeral_port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); - - // Verify that we actually got an ephemeral port. - ASSERT_NE(ephemeral_port, 0); - - // Verify that the ephemeral port is not reserved. - const FileDescriptor checking_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - EXPECT_THAT( - bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr), - connected_addr_len), - SyscallSucceeds()); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) { - auto const& param = GetParam(); - - for (int i = 0; true; i++) { - // Bind the v4 loopback on a v4 socket. - TestAddress const& test_addr = V4Loopback(); - sockaddr_storage bound_addr = test_addr.addr; - const FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - - // Listen iff TCP. - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); - } - - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Connect to bind an ephemeral port. - const FileDescriptor connected_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), - reinterpret_cast<sockaddr*>(&bound_addr), - bound_addr_len), - SyscallSucceeds()); - - // Get the ephemeral port. - sockaddr_storage connected_addr = {}; - socklen_t connected_addr_len = sizeof(connected_addr); - ASSERT_THAT(getsockname(connected_fd.get(), - reinterpret_cast<sockaddr*>(&connected_addr), - &connected_addr_len), - SyscallSucceeds()); - uint16_t const ephemeral_port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); - - // Verify that we actually got an ephemeral port. - ASSERT_NE(ephemeral_port, 0); - - // Verify that the ephemeral port is reserved. - const FileDescriptor checking_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - EXPECT_THAT( - bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr), - connected_addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v4 loopback on the same port with a v6 socket - // fails. - TestAddress const& test_addr_v4_mapped = V4MappedLoopback(); - sockaddr_storage addr_v4_mapped = test_addr_v4_mapped.addr; - ASSERT_NO_ERRNO(SetAddrPort(test_addr_v4_mapped.family(), &addr_v4_mapped, - ephemeral_port)); - const FileDescriptor fd_v4_mapped = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v4_mapped.family(), param.type, 0)); - EXPECT_THAT( - bind(fd_v4_mapped.get(), reinterpret_cast<sockaddr*>(&addr_v4_mapped), - test_addr_v4_mapped.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // Verify that binding the v6 any on the same port with a dual-stack socket - // fails. - TestAddress const& test_addr_v6_any = V6Any(); - sockaddr_storage addr_v6_any = test_addr_v6_any.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v6_any.family(), &addr_v6_any, ephemeral_port)); - const FileDescriptor fd_v6_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v6_any.family(), param.type, 0)); - ASSERT_THAT(bind(fd_v6_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any), - test_addr_v6_any.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); - - // For some reason, binding the TCP v6-only any is flaky on Linux. Maybe we - // tend to run out of ephemeral ports? Regardless, binding the v6 loopback - // seems pretty reliable. Only try to bind the v6-only any on UDP and - // gVisor. - - int ret = -1; - - if (!IsRunningOnGvisor() && param.type == SOCK_STREAM) { - // Verify that we can still bind the v6 loopback on the same port. - TestAddress const& test_addr_v6 = V6Loopback(); - sockaddr_storage addr_v6 = test_addr_v6.addr; - ASSERT_NO_ERRNO( - SetAddrPort(test_addr_v6.family(), &addr_v6, ephemeral_port)); - const FileDescriptor fd_v6 = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v6.family(), param.type, 0)); - ret = bind(fd_v6.get(), reinterpret_cast<sockaddr*>(&addr_v6), - test_addr_v6.addr_len); - } else { - // Verify that we can still bind the v6 any on the same port with a - // v6-only socket. - const FileDescriptor fd_v6_only_any = ASSERT_NO_ERRNO_AND_VALUE( - Socket(test_addr_v6_any.family(), param.type, 0)); - EXPECT_THAT(setsockopt(fd_v6_only_any.get(), IPPROTO_IPV6, IPV6_V6ONLY, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ret = - bind(fd_v6_only_any.get(), reinterpret_cast<sockaddr*>(&addr_v6_any), - test_addr_v6_any.addr_len); - } - - if (ret == -1 && errno == EADDRINUSE) { - // Port may have been in use. - ASSERT_LT(i, 100); // Give up after 100 tries. - continue; - } - EXPECT_THAT(ret, SyscallSucceeds()); - - // No need to try again. - break; - } -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReservedReuseAddr) { - auto const& param = GetParam(); - - // Bind the v4 loopback on a v4 socket. - TestAddress const& test_addr = V4Loopback(); - sockaddr_storage bound_addr = test_addr.addr; - const FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - - ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - - // Listen iff TCP. - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds()); - } - - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Connect to bind an ephemeral port. - const FileDescriptor connected_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - - ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), - reinterpret_cast<sockaddr*>(&bound_addr), - bound_addr_len), - SyscallSucceeds()); - - // Get the ephemeral port. - sockaddr_storage connected_addr = {}; - socklen_t connected_addr_len = sizeof(connected_addr); - ASSERT_THAT(getsockname(connected_fd.get(), - reinterpret_cast<sockaddr*>(&connected_addr), - &connected_addr_len), - SyscallSucceeds()); - uint16_t const ephemeral_port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr)); - - // Verify that we actually got an ephemeral port. - ASSERT_NE(ephemeral_port, 0); - - // Verify that the ephemeral port is not reserved. - const FileDescriptor checking_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - EXPECT_THAT( - bind(checking_fd.get(), reinterpret_cast<sockaddr*>(&connected_addr), - connected_addr_len), - SyscallSucceeds()); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, - MultipleBindsAllowedNoListeningReuseAddr) { - const auto& param = GetParam(); - // UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP - // this is only permitted if there is no other listening socket. - SKIP_IF(param.type != SOCK_STREAM); - // Bind the v4 loopback on a v4 socket. - const TestAddress& test_addr = V4Loopback(); - sockaddr_storage bound_addr = test_addr.addr; - FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - - ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - - // Now create a socket and bind it to the same port, this should - // succeed since there is no listening socket for the same port. - FileDescriptor second_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - - ASSERT_THAT(setsockopt(second_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(second_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len), - SyscallSucceeds()); -} - -TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) { - auto const& param = GetParam(); - TestAddress const& test_addr = V4Loopback(); - sockaddr_storage addr = test_addr.addr; - - for (int i = 0; i < 2; i++) { - const int portreuse1 = i % 2; - auto s1 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - int fd1 = s1.get(); - socklen_t addrlen = test_addr.addr_len; - - EXPECT_THAT( - setsockopt(fd1, SOL_SOCKET, SO_REUSEPORT, &portreuse1, sizeof(int)), - SyscallSucceeds()); - - ASSERT_THAT(bind(fd1, reinterpret_cast<sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(getsockname(fd1, reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - if (param.type == SOCK_STREAM) { - ASSERT_THAT(listen(fd1, 1), SyscallSucceeds()); - } - - // j is less than 4 to check that the port reuse logic works correctly after - // closing bound sockets. - for (int j = 0; j < 4; j++) { - const int portreuse2 = j % 2; - auto s2 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - int fd2 = s2.get(); - - EXPECT_THAT( - setsockopt(fd2, SOL_SOCKET, SO_REUSEPORT, &portreuse2, sizeof(int)), - SyscallSucceeds()); - - std::cout << portreuse1 << " " << portreuse2 << std::endl; - int ret = bind(fd2, reinterpret_cast<sockaddr*>(&addr), addrlen); - - // Verify that two sockets can be bound to the same port only if - // SO_REUSEPORT is set for both of them. - if (!portreuse1 || !portreuse2) { - ASSERT_THAT(ret, SyscallFailsWithErrno(EADDRINUSE)); - } else { - ASSERT_THAT(ret, SyscallSucceeds()); - } - } - } -} - -// Check that when a socket was bound to an address with REUSEPORT and then -// closed, we can bind a different socket to the same address without needing -// REUSEPORT. -TEST_P(SocketMultiProtocolInetLoopbackTest, NoReusePortFollowingReusePort) { - auto const& param = GetParam(); - TestAddress const& test_addr = V4Loopback(); - sockaddr_storage addr = test_addr.addr; - - auto s = ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - int fd = s.get(); - socklen_t addrlen = test_addr.addr_len; - int portreuse = 1; - ASSERT_THAT( - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &portreuse, sizeof(portreuse)), - SyscallSucceeds()); - ASSERT_THAT(bind(fd, reinterpret_cast<sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - ASSERT_THAT(getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - ASSERT_EQ(addrlen, test_addr.addr_len); - - s.reset(); - - // Open a new socket and bind to the same address, but w/o REUSEPORT. - s = ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - fd = s.get(); - portreuse = 0; - ASSERT_THAT( - setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &portreuse, sizeof(portreuse)), - SyscallSucceeds()); - ASSERT_THAT(bind(fd, reinterpret_cast<sockaddr*>(&addr), addrlen), - SyscallSucceeds()); -} - -INSTANTIATE_TEST_SUITE_P( - AllFamilies, SocketMultiProtocolInetLoopbackTest, - ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM}, - ProtocolTestParam{"UDP", SOCK_DGRAM}), - DescribeProtocolTestParam); - -} // namespace - -// Check that loopback receives connections from any address in the range: -// 127.0.0.1 to 127.254.255.255. This behavior is exclusive to IPv4. -TEST_F(SocketInetLoopbackTest, LoopbackAddressRangeConnect) { - TestAddress const& listener = V4Any(); - - in_addr_t addresses[] = { - INADDR_LOOPBACK, - INADDR_LOOPBACK + 1, // 127.0.0.2 - (in_addr_t)0x7f000101, // 127.0.1.1 - (in_addr_t)0x7f010101, // 127.1.1.1 - (in_addr_t)0x7ffeffff, // 127.254.255.255 - }; - for (const auto& address : addresses) { - TestAddress connector("V4Loopback"); - connector.addr.ss_family = AF_INET; - connector.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&connector.addr)->sin_addr.s_addr = - htonl(address); - - tcpSimpleConnectTest(listener, connector, true); - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc deleted file mode 100644 index 1a0b53394..000000000 --- a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <string.h> - -#include <iostream> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::Gt; - -PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) { - switch (family) { - case AF_INET: - return static_cast<uint16_t>( - reinterpret_cast<sockaddr_in const*>(&addr)->sin_port); - case AF_INET6: - return static_cast<uint16_t>( - reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port); - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } -} - -PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) { - switch (family) { - case AF_INET: - reinterpret_cast<sockaddr_in*>(addr)->sin_port = port; - return NoError(); - case AF_INET6: - reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port; - return NoError(); - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } -} - -struct TestParam { - TestAddress listener; - TestAddress connector; -}; - -std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) { - return absl::StrCat("Listen", info.param.listener.description, "_Connect", - info.param.connector.description); -} - -using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>; - -// This test verifies that connect returns EADDRNOTAVAIL if all local ephemeral -// ports are already in use for a given destination ip/port. -// -// We disable S/R because this test creates a large number of sockets. -// -// FIXME(b/162475855): This test is failing reliably. -TEST_P(SocketInetLoopbackTest, DISABLED_TestTCPPortExhaustion_NoRandomSave) { - auto const& param = GetParam(); - TestAddress const& listener = param.listener; - TestAddress const& connector = param.connector; - - constexpr int kBacklog = 10; - constexpr int kClients = 65536; - - // Create the listening socket. - auto 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(), kBacklog), 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()); - uint16_t const port = - ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr)); - - // Disable cooperative S/R as we are making too many syscalls. - DisableSave ds; - - // Now we keep opening connections till we run out of local ephemeral ports. - // and assert the error we get back. - sockaddr_storage conn_addr = connector.addr; - ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); - std::vector<FileDescriptor> clients; - std::vector<FileDescriptor> servers; - - for (int i = 0; i < kClients; i++) { - FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE( - Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); - int ret = connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr), - connector.addr_len); - if (ret == 0) { - clients.push_back(std::move(client)); - FileDescriptor server = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr)); - servers.push_back(std::move(server)); - continue; - } - ASSERT_THAT(ret, SyscallFailsWithErrno(EADDRNOTAVAIL)); - break; - } -} - -INSTANTIATE_TEST_SUITE_P( - All, SocketInetLoopbackTest, - ::testing::Values( - // Listeners bound to IPv4 addresses refuse connections using IPv6 - // addresses. - TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()}, - TestParam{V4Any(), V4MappedAny()}, - TestParam{V4Any(), V4MappedLoopback()}, - TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()}, - TestParam{V4Loopback(), V4MappedLoopback()}, - TestParam{V4MappedAny(), V4Any()}, - TestParam{V4MappedAny(), V4Loopback()}, - TestParam{V4MappedAny(), V4MappedAny()}, - TestParam{V4MappedAny(), V4MappedLoopback()}, - TestParam{V4MappedLoopback(), V4Any()}, - TestParam{V4MappedLoopback(), V4Loopback()}, - TestParam{V4MappedLoopback(), V4MappedLoopback()}, - - // Listeners bound to IN6ADDR_ANY accept all connections. - TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()}, - TestParam{V6Any(), V4MappedAny()}, - TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()}, - TestParam{V6Any(), V6Loopback()}, - - // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4 - // addresses. - TestParam{V6Loopback(), V6Any()}, - TestParam{V6Loopback(), V6Loopback()}), - DescribeTestParam); - -struct ProtocolTestParam { - std::string description; - int type; -}; - -std::string DescribeProtocolTestParam( - ::testing::TestParamInfo<ProtocolTestParam> const& info) { - return info.param.description; -} - -using SocketMultiProtocolInetLoopbackTest = - ::testing::TestWithParam<ProtocolTestParam>; - -TEST_P(SocketMultiProtocolInetLoopbackTest, - BindAvoidsListeningPortsReuseAddr_NoRandomSave) { - const auto& param = GetParam(); - // UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP - // this is only permitted if there is no other listening socket. - SKIP_IF(param.type != SOCK_STREAM); - - DisableSave ds; // Too many syscalls. - - // A map of port to file descriptor binding the port. - std::map<uint16_t, FileDescriptor> listen_sockets; - - // Exhaust all ephemeral ports. - while (true) { - // Bind the v4 loopback on a v4 socket. - TestAddress const& test_addr = V4Loopback(); - sockaddr_storage bound_addr = test_addr.addr; - FileDescriptor bound_fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0)); - - ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int ret = bind(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - test_addr.addr_len); - if (ret != 0) { - ASSERT_EQ(errno, EADDRINUSE); - break; - } - // Get the port that we bound. - socklen_t bound_addr_len = test_addr.addr_len; - ASSERT_THAT( - getsockname(bound_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr), - &bound_addr_len), - SyscallSucceeds()); - uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port; - - // Newly bound port should not already be in use by a listening socket. - ASSERT_EQ(listen_sockets.find(port), listen_sockets.end()); - auto fd = bound_fd.get(); - listen_sockets.insert(std::make_pair(port, std::move(bound_fd))); - ASSERT_THAT(listen(fd, SOMAXCONN), SyscallSucceeds()); - } -} - -INSTANTIATE_TEST_SUITE_P( - AllFamilies, SocketMultiProtocolInetLoopbackTest, - ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM}, - ProtocolTestParam{"UDP", SOCK_DGRAM}), - DescribeProtocolTestParam); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_loopback_blocking.cc b/test/syscalls/linux/socket_ip_loopback_blocking.cc deleted file mode 100644 index fda252dd7..000000000 --- a/test/syscalls/linux/socket_ip_loopback_blocking.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <netinet/tcp.h> - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>( - std::vector<SocketPairKind>{ - IPv6UDPBidirectionalBindSocketPair(0), - IPv4UDPBidirectionalBindSocketPair(0), - }, - ApplyVecToVec<SocketPairKind>( - std::vector<Middleware>{ - NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, - std::vector<SocketPairKind>{ - IPv6TCPAcceptBindSocketPair(0), - IPv4TCPAcceptBindSocketPair(0), - })); -} - -INSTANTIATE_TEST_SUITE_P( - BlockingIPSockets, BlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc deleted file mode 100644 index f10f55b27..000000000 --- a/test/syscalls/linux/socket_ip_tcp_generic.cc +++ /dev/null @@ -1,1308 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_ip_tcp_generic.h" - -#include <fcntl.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#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" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -using ::testing::AnyOf; -using ::testing::Eq; - -TEST_P(TCPSocketPairTest, TcpInfoSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct tcp_info opt = {}; - socklen_t optLen = sizeof(opt); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), - SyscallSucceeds()); -} - -TEST_P(TCPSocketPairTest, ShortTcpInfoSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct tcp_info opt = {}; - socklen_t optLen = 1; - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), - SyscallSucceeds()); -} - -TEST_P(TCPSocketPairTest, ZeroTcpInfoSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct tcp_info opt = {}; - socklen_t optLen = 0; - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), - SyscallSucceeds()); -} - -// Copied from include/net/tcp.h. -constexpr int TCP_CA_OPEN = 0; - -TEST_P(TCPSocketPairTest, CheckTcpInfoFields) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until second_fd sees the data and then recv it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; - constexpr int kPollTimeoutMs = 2000; // Wait up to 2 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - struct tcp_info opt = {}; - socklen_t optLen = sizeof(opt); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_TCP, TCP_INFO, &opt, &optLen), - SyscallSucceeds()); - ASSERT_EQ(optLen, sizeof(opt)); - - // Validates the received tcp_info fields. - EXPECT_EQ(opt.tcpi_ca_state, TCP_CA_OPEN); - EXPECT_GT(opt.tcpi_snd_cwnd, 0); - EXPECT_GT(opt.tcpi_rto, 0); -} - -// This test validates that an RST is sent instead of a FIN when data is -// unread on calls to close(2). -TEST_P(TCPSocketPairTest, RSTSentOnCloseWithUnreadData) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until t_ sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now close the connected without reading the data. - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); - - // Wait for the other end to receive the RST (up to 20 seconds). - struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // A shutdown with unread data will cause a RST to be sent instead - // of a FIN, per RFC 2525 section 2.17; this is also what Linux does. - ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(ECONNRESET)); -} - -// This test will validate that a RST will cause POLLHUP to trigger. -TEST_P(TCPSocketPairTest, RSTCausesPollHUP) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until second sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(poll_fd.revents & POLLIN, POLLIN); - - // Confirm we at least have one unread byte. - int bytes_available = 0; - ASSERT_THAT( - RetryEINTR(ioctl)(sockets->second_fd(), FIONREAD, &bytes_available), - SyscallSucceeds()); - EXPECT_GT(bytes_available, 0); - - // Now close the connected socket without reading the data from the second, - // this will cause a RST and we should see that with POLLHUP. - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); - - // Wait for the other end to receive the RST (up to 20 seconds). - struct pollfd poll_fd3 = {sockets->first_fd(), POLLHUP, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd3, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - ASSERT_NE(poll_fd3.revents & POLLHUP, 0); -} - -// This test validates that even if a RST is sent the other end will not -// get an ECONNRESET until it's read all data. -TEST_P(TCPSocketPairTest, RSTSentOnCloseWithUnreadDataAllowsReadBuffered) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until second sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; - constexpr int kPollTimeoutMs = 30000; // Wait up to 30 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Wait until first sees the data on its side but don't read it. - struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now close the connected socket without reading the data from the second. - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); - - // Wait for the other end to receive the RST (up to 30 seconds). - struct pollfd poll_fd3 = {sockets->first_fd(), POLLHUP, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd3, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Since we also have data buffered we should be able to read it before - // the syscall will fail with ECONNRESET. - ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // A shutdown with unread data will cause a RST to be sent instead - // of a FIN, per RFC 2525 section 2.17; this is also what Linux does. - ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(ECONNRESET)); -} - -// This test will verify that a clean shutdown (FIN) is preformed when there -// is unread data but only the write side is closed. -TEST_P(TCPSocketPairTest, FINSentOnShutdownWrWithUnreadData) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until t_ sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now shutdown the write end leaving the read end open. - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_WR), SyscallSucceeds()); - - // Wait for the other end to receive the FIN (up to 20 seconds). - struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Since we didn't shutdown the read end this will be a clean close. - ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); -} - -// This test will verify that when data is received by a socket, even if it's -// not read SHUT_RD will not cause any packets to be generated. -TEST_P(TCPSocketPairTest, ShutdownRdShouldCauseNoPacketsWithUnreadData) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until t_ sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now shutdown the read end, this will generate no packets to the other end. - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds()); - - // We should not receive any events on the other side of the socket. - struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollNoResponseTimeoutMs = 3000; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollNoResponseTimeoutMs), - SyscallSucceedsWithValue(0)); // Timeout. -} - -// This test will verify that a socket which has unread data will still allow -// the data to be read after shutting down the read side, and once there is no -// unread data left, then read will return an EOF. -TEST_P(TCPSocketPairTest, ShutdownRdAllowsReadOfReceivedDataBeforeEOF) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until t_ sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now shutdown the read end. - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds()); - - // Even though we did a SHUT_RD on the read end we can still read the data. - ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // After reading all of the data, reading the closed read end returns EOF. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); -} - -// This test verifies that a shutdown(wr) by the server after sending -// data allows the client to still read() the queued data and a client -// close after sending response allows server to read the incoming -// response. -TEST_P(TCPSocketPairTest, ShutdownWrServerClientClose) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char buf[10] = {}; - ScopedThread t([&]() { - ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(close(sockets->release_first_fd()), - SyscallSucceedsWithValue(0)); - }); - ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(RetryEINTR(shutdown)(sockets->second_fd(), SHUT_WR), - SyscallSucceedsWithValue(0)); - t.Join(); - - ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST_P(TCPSocketPairTest, ClosedReadNonBlockingSocket) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Set the read end to O_NONBLOCK. - int opts = 0; - ASSERT_THAT(opts = fcntl(sockets->second_fd(), F_GETFL), SyscallSucceeds()); - ASSERT_THAT(fcntl(sockets->second_fd(), F_SETFL, opts | O_NONBLOCK), - SyscallSucceeds()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until second_fd sees the data and then recv it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN, 0}; - constexpr int kPollTimeoutMs = 2000; // Wait up to 2 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - // Now shutdown the write end leaving the read end open. - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - - // Wait for close notification and recv again. - struct pollfd poll_fd2 = {sockets->second_fd(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(0)); -} - -TEST_P(TCPSocketPairTest, - ShutdownRdUnreadDataShouldCauseNoPacketsUnlessClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // Wait until t_ sees the data on its side but don't read it. - struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollTimeoutMs = 20000; // Wait up to 20 seconds for the data. - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - // Now shutdown the read end, this will generate no packets to the other end. - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RD), SyscallSucceeds()); - - // We should not receive any events on the other side of the socket. - struct pollfd poll_fd2 = {sockets->first_fd(), POLLIN | POLLHUP, 0}; - constexpr int kPollNoResponseTimeoutMs = 3000; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollNoResponseTimeoutMs), - SyscallSucceedsWithValue(0)); // Timeout. - - // Now since we've fully closed the connection it will generate a RST. - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(poll)(&poll_fd2, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); // The other end has closed. - - // A shutdown with unread data will cause a RST to be sent instead - // of a FIN, per RFC 2525 section 2.17; this is also what Linux does. - ASSERT_THAT(RetryEINTR(read)(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(ECONNRESET)); -} - -TEST_P(TCPSocketPairTest, TCPCorkDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(TCPSocketPairTest, SetTCPCork) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(TCPSocketPairTest, TCPCork) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - constexpr char kData[] = "abc"; - ASSERT_THAT(WriteFd(sockets->first_fd(), kData, sizeof(kData)), - SyscallSucceedsWithValue(sizeof(kData))); - - ASSERT_NO_FATAL_FAILURE(RecvNoData(sockets->second_fd())); - - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CORK, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - // Create a receive buffer larger than kData. - char buf[(sizeof(kData) + 1) * 2] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kData))); - EXPECT_EQ(absl::string_view(kData, sizeof(kData)), - absl::string_view(buf, sizeof(kData))); -} - -TEST_P(TCPSocketPairTest, TCPQuickAckDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -TEST_P(TCPSocketPairTest, SetTCPQuickAck) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_QUICKACK, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -TEST_P(TCPSocketPairTest, SoKeepaliveDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(TCPSocketPairTest, SetSoKeepalive) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_KEEPALIVE, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(TCPSocketPairTest, TCPKeepidleDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 2 * 60 * 60); // 2 hours. -} - -TEST_P(TCPSocketPairTest, TCPKeepintvlDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 75); // 75 seconds. -} - -TEST_P(TCPSocketPairTest, SetTCPKeepidleZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, &kZero, - sizeof(kZero)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepintvlZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, - &kZero, sizeof(kZero)), - SyscallFailsWithErrno(EINVAL)); -} - -// Copied from include/net/tcp.h. -constexpr int MAX_TCP_KEEPIDLE = 32767; -constexpr int MAX_TCP_KEEPINTVL = 32767; -constexpr int MAX_TCP_KEEPCNT = 127; - -TEST_P(TCPSocketPairTest, SetTCPKeepidleAboveMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kAboveMax = MAX_TCP_KEEPIDLE + 1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, - &kAboveMax, sizeof(kAboveMax)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepintvlAboveMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kAboveMax = MAX_TCP_KEEPINTVL + 1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, - &kAboveMax, sizeof(kAboveMax)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepidleToMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, - &MAX_TCP_KEEPIDLE, sizeof(MAX_TCP_KEEPIDLE)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPIDLE, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, MAX_TCP_KEEPIDLE); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepintvlToMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, - &MAX_TCP_KEEPINTVL, sizeof(MAX_TCP_KEEPINTVL)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPINTVL, &get, - &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, MAX_TCP_KEEPINTVL); -} - -TEST_P(TCPSocketPairTest, TCPKeepcountDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 9); // 9 keepalive probes. -} - -TEST_P(TCPSocketPairTest, SetTCPKeepcountZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, &kZero, - sizeof(kZero)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepcountAboveMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kAboveMax = MAX_TCP_KEEPCNT + 1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, - &kAboveMax, sizeof(kAboveMax)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepcountToMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, - &MAX_TCP_KEEPCNT, sizeof(MAX_TCP_KEEPCNT)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, MAX_TCP_KEEPCNT); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepcountToOne) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int keepaliveCount = 1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, - &keepaliveCount, sizeof(keepaliveCount)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, keepaliveCount); -} - -TEST_P(TCPSocketPairTest, SetTCPKeepcountToNegative) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int keepaliveCount = -5; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_KEEPCNT, - &keepaliveCount, sizeof(keepaliveCount)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(TCPSocketPairTest, SetOOBInline) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_OOBINLINE, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_OOBINLINE, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -TEST_P(TCPSocketPairTest, MsgTruncMsgPeek) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - // Read half of the data with MSG_TRUNC | MSG_PEEK. This way there will still - // be some data left to read in the next step even if the data gets consumed. - char received_data1[sizeof(sent_data) / 2] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data1, - sizeof(received_data1), MSG_TRUNC | MSG_PEEK), - SyscallSucceedsWithValue(sizeof(received_data1))); - - // Check that we didn't get anything. - char zeros[sizeof(received_data1)] = {}; - EXPECT_EQ(0, memcmp(zeros, received_data1, sizeof(received_data1))); - - // Check that all of the data is still there. - char received_data2[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data2, - sizeof(received_data2), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - EXPECT_EQ(0, memcmp(received_data2, sent_data, sizeof(sent_data))); -} - -TEST_P(TCPSocketPairTest, SetCongestionControlSucceedsForSupported) { - // This is Linux's net/tcp.h TCP_CA_NAME_MAX. - const int kTcpCaNameMax = 16; - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - // Netstack only supports reno & cubic so we only test these two values here. - { - const char kSetCC[kTcpCaNameMax] = "reno"; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &kSetCC, strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[kTcpCaNameMax]; - memset(got_cc, '1', sizeof(got_cc)); - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(kSetCC))); - } - { - const char kSetCC[kTcpCaNameMax] = "cubic"; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &kSetCC, strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[kTcpCaNameMax]; - memset(got_cc, '1', sizeof(got_cc)); - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(kSetCC))); - } -} - -TEST_P(TCPSocketPairTest, SetGetTCPCongestionShortReadBuffer) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - { - // Verify that getsockopt/setsockopt work with buffers smaller than - // kTcpCaNameMax. - const char kSetCC[] = "cubic"; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &kSetCC, strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[sizeof(kSetCC)]; - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(got_cc))); - } -} - -TEST_P(TCPSocketPairTest, SetGetTCPCongestionLargeReadBuffer) { - // This is Linux's net/tcp.h TCP_CA_NAME_MAX. - const int kTcpCaNameMax = 16; - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - { - // Verify that getsockopt works with buffers larger than - // kTcpCaNameMax. - const char kSetCC[] = "cubic"; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &kSetCC, strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[kTcpCaNameMax + 5]; - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - // Linux copies the minimum of kTcpCaNameMax or the length of the passed in - // buffer and sets optlen to the number of bytes actually copied - // irrespective of the actual length of the congestion control name. - EXPECT_EQ(kTcpCaNameMax, optlen); - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(kSetCC))); - } -} - -TEST_P(TCPSocketPairTest, SetCongestionControlFailsForUnsupported) { - // This is Linux's net/tcp.h TCP_CA_NAME_MAX. - const int kTcpCaNameMax = 16; - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char old_cc[kTcpCaNameMax]; - socklen_t optlen = sizeof(old_cc); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &old_cc, &optlen), - SyscallSucceedsWithValue(0)); - - const char kSetCC[] = "invalid_ca_cc"; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &kSetCC, strlen(kSetCC)), - SyscallFailsWithErrno(ENOENT)); - - char got_cc[kTcpCaNameMax]; - optlen = sizeof(got_cc); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_CONGESTION, - &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(0, memcmp(got_cc, old_cc, sizeof(old_cc))); -} - -// Linux and Netstack both default to a 60s TCP_LINGER2 timeout. -constexpr int kDefaultTCPLingerTimeout = 60; -// On Linux, the maximum linger2 timeout was changed from 60sec to 120sec. -constexpr int kMaxTCPLingerTimeout = 120; -constexpr int kOldMaxTCPLingerTimeout = 60; - -TEST_P(TCPSocketPairTest, TCPLingerTimeoutDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kDefaultTCPLingerTimeout); -} - -TEST_P(TCPSocketPairTest, SetTCPLingerTimeoutLessThanZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kNegative = -1234; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, - &kNegative, sizeof(kNegative)), - SyscallSucceedsWithValue(0)); - int get = INT_MAX; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, -1); -} - -TEST_P(TCPSocketPairTest, SetTCPLingerTimeoutZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, &kZero, - sizeof(kZero)), - SyscallSucceedsWithValue(0)); - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_THAT(get, - AnyOf(Eq(kMaxTCPLingerTimeout), Eq(kOldMaxTCPLingerTimeout))); -} - -TEST_P(TCPSocketPairTest, SetTCPLingerTimeoutAboveMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Values above the net.ipv4.tcp_fin_timeout are capped to tcp_fin_timeout - // on linux (defaults to 60 seconds on linux). - constexpr int kAboveDefault = kMaxTCPLingerTimeout + 1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, - &kAboveDefault, sizeof(kAboveDefault)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - if (IsRunningOnGvisor()) { - EXPECT_EQ(get, kMaxTCPLingerTimeout); - } else { - EXPECT_THAT(get, - AnyOf(Eq(kMaxTCPLingerTimeout), Eq(kOldMaxTCPLingerTimeout))); - } -} - -TEST_P(TCPSocketPairTest, SetTCPLingerTimeout) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Values above the net.ipv4.tcp_fin_timeout are capped to tcp_fin_timeout - // on linux (defaults to 60 seconds on linux). - constexpr int kTCPLingerTimeout = kDefaultTCPLingerTimeout - 1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, - &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT( - getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_LINGER2, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kTCPLingerTimeout); -} - -TEST_P(TCPSocketPairTest, TestTCPCloseWithData) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ScopedThread t([&]() { - // Close one end to trigger sending of a FIN. - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_WR), SyscallSucceeds()); - char buf[3]; - ASSERT_THAT(read(sockets->second_fd(), buf, 3), - SyscallSucceedsWithValue(3)); - absl::SleepFor(absl::Milliseconds(50)); - ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds()); - }); - - absl::SleepFor(absl::Milliseconds(50)); - // Send some data then close. - constexpr char kStr[] = "abc"; - ASSERT_THAT(write(sockets->first_fd(), kStr, 3), SyscallSucceedsWithValue(3)); - t.Join(); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); -} - -TEST_P(TCPSocketPairTest, TCPUserTimeoutDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 0); // 0 ms (disabled). -} - -TEST_P(TCPSocketPairTest, SetTCPUserTimeoutZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kZero = 0; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &kZero, sizeof(kZero)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 0); // 0 ms (disabled). -} - -TEST_P(TCPSocketPairTest, SetTCPUserTimeoutBelowZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kNeg = -10; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &kNeg, sizeof(kNeg)), - SyscallFailsWithErrno(EINVAL)); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 0); // 0 ms (disabled). -} - -TEST_P(TCPSocketPairTest, SetTCPUserTimeoutAboveZero) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kAbove = 10; - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &kAbove, sizeof(kAbove)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kAbove); -} - -#ifdef __linux__ -TEST_P(TCPSocketPairTest, SpliceFromPipe) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - FileDescriptor rfd(fds[0]); - FileDescriptor wfd(fds[1]); - - // Fill with some random data. - std::vector<char> buf(kPageSize / 2); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - EXPECT_THAT( - splice(rfd.get(), nullptr, sockets->first_fd(), nullptr, kPageSize, 0), - SyscallSucceedsWithValue(buf.size())); - - std::vector<char> rbuf(buf.size()); - ASSERT_THAT(read(sockets->second_fd(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(buf.size())); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); -} - -TEST_P(TCPSocketPairTest, SpliceToPipe) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - FileDescriptor rfd(fds[0]); - FileDescriptor wfd(fds[1]); - - // Fill with some random data. - std::vector<char> buf(kPageSize / 2); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(sockets->first_fd(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - shutdown(sockets->first_fd(), SHUT_WR); - EXPECT_THAT( - splice(sockets->second_fd(), nullptr, wfd.get(), nullptr, kPageSize, 0), - SyscallSucceedsWithValue(buf.size())); - - std::vector<char> rbuf(buf.size()); - ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(buf.size())); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); -} - -#include <sys/sendfile.h> - -TEST_P(TCPSocketPairTest, SendfileFromRegularFileSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - // Fill with some random data. - std::vector<char> buf(kPageSize / 2); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(pwrite(in_fd.get(), buf.data(), buf.size(), 0), - SyscallSucceedsWithValue(buf.size())); - - EXPECT_THAT( - sendfile(sockets->first_fd(), in_fd.get(), nullptr, buf.size() + 1), - SyscallSucceedsWithValue(buf.size())); - std::vector<char> rbuf(buf.size() + 1); - ASSERT_THAT(read(sockets->second_fd(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(buf.size())); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); -} -#endif // __linux__ - -TEST_P(TCPSocketPairTest, SetTCPWindowClampBelowMinRcvBufConnectedSocket) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - // Discover minimum receive buf by setting a really low value - // for the receive buffer. - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &kZero, - sizeof(kZero)), - SyscallSucceeds()); - - // Now retrieve the minimum value for SO_RCVBUF as the set above should - // have caused SO_RCVBUF for the socket to be set to the minimum. - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - int min_so_rcvbuf = get; - - { - // Setting TCP_WINDOW_CLAMP to zero for a connected socket is not permitted. - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_WINDOW_CLAMP, - &kZero, sizeof(kZero)), - SyscallFailsWithErrno(EINVAL)); - - // Non-zero clamp values below MIN_SO_RCVBUF/2 should result in the clamp - // being set to MIN_SO_RCVBUF/2. - int below_half_min_so_rcvbuf = min_so_rcvbuf / 2 - 1; - EXPECT_THAT( - setsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_WINDOW_CLAMP, - &below_half_min_so_rcvbuf, sizeof(below_half_min_so_rcvbuf)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_TCP, TCP_WINDOW_CLAMP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(min_so_rcvbuf / 2, get); - } -} - -TEST_P(TCPSocketPairTest, IpMulticastTtlDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_GT(get, 0); -} - -TEST_P(TCPSocketPairTest, IpMulticastLoopDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 1); -} - -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(); - } -} - -// Test setsockopt and getsockopt for a socket with SO_LINGER option. -TEST_P(TCPSocketPairTest, SetAndGetLingerOption) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Check getsockopt before SO_LINGER option is set. - struct linger got_linger = {-1, -1}; - socklen_t got_len = sizeof(got_linger); - - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &got_len), - SyscallSucceeds()); - ASSERT_THAT(got_len, sizeof(got_linger)); - struct linger want_linger = {}; - EXPECT_EQ(0, memcmp(&want_linger, &got_linger, got_len)); - - // Set and get SO_LINGER with negative values. - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = -3; - ASSERT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &got_len), - SyscallSucceeds()); - ASSERT_EQ(got_len, sizeof(got_linger)); - EXPECT_EQ(sl.l_onoff, got_linger.l_onoff); - // Linux returns a different value as it uses HZ to convert the seconds to - // jiffies which overflows for negative values. We want to be compatible with - // linux for getsockopt return value. - if (IsRunningOnGvisor()) { - EXPECT_EQ(sl.l_linger, got_linger.l_linger); - } - - // Set and get SO_LINGER option with positive values. - sl.l_onoff = 1; - sl.l_linger = 5; - ASSERT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &got_len), - SyscallSucceeds()); - ASSERT_EQ(got_len, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len)); -} - -// Test socket to disable SO_LINGER option. -TEST_P(TCPSocketPairTest, SetOffLingerOption) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Set the SO_LINGER option. - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = 5; - ASSERT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - - // Check getsockopt after SO_LINGER option is set. - struct linger got_linger = {-1, -1}; - socklen_t got_len = sizeof(got_linger); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &got_len), - SyscallSucceeds()); - ASSERT_EQ(got_len, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len)); - - sl.l_onoff = 0; - sl.l_linger = 5; - ASSERT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - - // Check getsockopt after SO_LINGER option is set to zero. - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &got_len), - SyscallSucceeds()); - ASSERT_EQ(got_len, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len)); -} - -// Test close on dup'd socket with SO_LINGER option set. -TEST_P(TCPSocketPairTest, CloseWithLingerOption) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Set the SO_LINGER option. - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = 5; - ASSERT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - - // Check getsockopt after SO_LINGER option is set. - struct linger got_linger = {-1, -1}; - socklen_t got_len = sizeof(got_linger); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &got_len), - SyscallSucceeds()); - ASSERT_EQ(got_len, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, got_len)); - - FileDescriptor dupFd = FileDescriptor(dup(sockets->first_fd())); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - char buf[10] = {}; - // Write on dupFd should succeed as socket will not be closed until - // all references are removed. - ASSERT_THAT(RetryEINTR(write)(dupFd.get(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(RetryEINTR(write)(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(EBADF)); - - // Close the socket. - dupFd.reset(); - // Write on dupFd should fail as all references for socket are removed. - ASSERT_THAT(RetryEINTR(write)(dupFd.get(), buf, sizeof(buf)), - SyscallFailsWithErrno(EBADF)); -} -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_generic.h b/test/syscalls/linux/socket_ip_tcp_generic.h deleted file mode 100644 index a3eff3c73..000000000 --- a/test/syscalls/linux/socket_ip_tcp_generic.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected TCP sockets. -using TCPSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_ diff --git a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc deleted file mode 100644 index 4e79d21f4..000000000 --- a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <netinet/tcp.h> - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_ip_tcp_generic.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVecToVec<SocketPairKind>( - std::vector<Middleware>{ - NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, - std::vector<SocketPairKind>{ - IPv6TCPAcceptBindSocketPair(0), - IPv4TCPAcceptBindSocketPair(0), - DualStackTCPAcceptBindSocketPair(0), - }); -} - -INSTANTIATE_TEST_SUITE_P( - AllTCPSockets, TCPSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_loopback.cc b/test/syscalls/linux/socket_ip_tcp_loopback.cc deleted file mode 100644 index 9db3037bc..000000000 --- a/test/syscalls/linux/socket_ip_tcp_loopback.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_generic.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return { - IPv6TCPAcceptBindSocketPair(0), - IPv4TCPAcceptBindSocketPair(0), - DualStackTCPAcceptBindSocketPair(0), - }; -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, AllSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc deleted file mode 100644 index f996b93d2..000000000 --- a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <netinet/tcp.h> - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_stream_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVecToVec<SocketPairKind>( - std::vector<Middleware>{ - NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, - std::vector<SocketPairKind>{ - IPv6TCPAcceptBindSocketPair(0), - IPv4TCPAcceptBindSocketPair(0), - DualStackTCPAcceptBindSocketPair(0), - }); -} - -INSTANTIATE_TEST_SUITE_P( - BlockingTCPSockets, BlockingStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc deleted file mode 100644 index ffa377210..000000000 --- a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <netinet/tcp.h> - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_non_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVecToVec<SocketPairKind>( - std::vector<Middleware>{ - NoOp, SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &kSockOptOn)}, - std::vector<SocketPairKind>{ - IPv6TCPAcceptBindSocketPair(SOCK_NONBLOCK), - IPv4TCPAcceptBindSocketPair(SOCK_NONBLOCK), - }); -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingTCPSockets, NonBlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc deleted file mode 100644 index f178f1af9..000000000 --- a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of TCP and UDP sockets. -using TcpUdpSocketPairTest = SocketPairTest; - -TEST_P(TcpUdpSocketPairTest, ShutdownWrFollowedBySendIsError) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Now shutdown the write end of the first. - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_WR), SyscallSucceeds()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EPIPE)); -} - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - IPv6UDPBidirectionalBindSocketPair, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - IPv4UDPBidirectionalBindSocketPair, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - DualStackUDPBidirectionalBindSocketPair, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - IPv6TCPAcceptBindSocketPair, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - IPv4TCPAcceptBindSocketPair, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - DualStackTCPAcceptBindSocketPair, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK}))); -} - -INSTANTIATE_TEST_SUITE_P( - AllIPSockets, TcpUdpSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc deleted file mode 100644 index 1694e188a..000000000 --- a/test/syscalls/linux/socket_ip_udp_generic.cc +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_ip_udp_generic.h" - -#include <errno.h> -#ifdef __linux__ -#include <linux/in6.h> -#endif // __linux__ -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -TEST_P(UDPSocketPairTest, MulticastTTLDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 1); -} - -TEST_P(UDPSocketPairTest, SetUDPMulticastTTLMin) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kMin = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kMin, sizeof(kMin)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kMin); -} - -TEST_P(UDPSocketPairTest, SetUDPMulticastTTLMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kMax = 255; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kMax, sizeof(kMax)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kMax); -} - -TEST_P(UDPSocketPairTest, SetUDPMulticastTTLNegativeOne) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kArbitrary = 6; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kArbitrary, sizeof(kArbitrary)), - SyscallSucceeds()); - - constexpr int kNegOne = -1; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kNegOne, sizeof(kNegOne)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, 1); -} - -TEST_P(UDPSocketPairTest, SetUDPMulticastTTLBelowMin) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kBelowMin = -2; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kBelowMin, sizeof(kBelowMin)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(UDPSocketPairTest, SetUDPMulticastTTLAboveMax) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr int kAboveMax = 256; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kAboveMax, sizeof(kAboveMax)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(UDPSocketPairTest, SetUDPMulticastTTLChar) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr char kArbitrary = 6; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &kArbitrary, sizeof(kArbitrary)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_TTL, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kArbitrary); -} - -TEST_P(UDPSocketPairTest, SetEmptyIPAddMembership) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct ip_mreqn req = {}; - EXPECT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &req, sizeof(req)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(UDPSocketPairTest, MulticastLoopDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -TEST_P(UDPSocketPairTest, SetMulticastLoop) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -TEST_P(UDPSocketPairTest, SetMulticastLoopChar) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - constexpr char kSockOptOnChar = kSockOptOn; - constexpr char kSockOptOffChar = kSockOptOff; - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOffChar, sizeof(kSockOptOffChar)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); - - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOnChar, sizeof(kSockOptOnChar)), - SyscallSucceeds()); - - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_LOOP, - &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -TEST_P(UDPSocketPairTest, ReuseAddrDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(UDPSocketPairTest, SetReuseAddr) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(UDPSocketPairTest, ReusePortDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(UDPSocketPairTest, SetReusePort) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(UDPSocketPairTest, SetReuseAddrReusePort) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEADDR, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); - - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_REUSEPORT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -// Test getsockopt for a socket which is not set with IP_PKTINFO option. -TEST_P(UDPSocketPairTest, IPPKTINFODefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_IP, IP_PKTINFO, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -// Test setsockopt and getsockopt for a socket with IP_PKTINFO option. -TEST_P(UDPSocketPairTest, SetAndGetIPPKTINFO) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int level = SOL_IP; - int type = IP_PKTINFO; - - // Check getsockopt before IP_PKTINFO is set. - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get, kSockOptOn); - EXPECT_EQ(get_len, sizeof(get)); - - ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOff, - sizeof(kSockOptOff)), - SyscallSucceedsWithValue(0)); - - ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get, kSockOptOff); - EXPECT_EQ(get_len, sizeof(get)); -} - -// Test getsockopt for a socket which is not set with IP_RECVORIGDSTADDR option. -TEST_P(UDPSocketPairTest, ReceiveOrigDstAddrDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - int level = SOL_IP; - int type = IP_RECVORIGDSTADDR; - if (sockets->first_addr()->sa_family == AF_INET6) { - level = SOL_IPV6; - type = IPV6_RECVORIGDSTADDR; - } - ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -// Test setsockopt and getsockopt for a socket with IP_RECVORIGDSTADDR option. -TEST_P(UDPSocketPairTest, SetAndGetReceiveOrigDstAddr) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int level = SOL_IP; - int type = IP_RECVORIGDSTADDR; - if (sockets->first_addr()->sa_family == AF_INET6) { - level = SOL_IPV6; - type = IPV6_RECVORIGDSTADDR; - } - - // Check getsockopt before IP_PKTINFO is set. - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get, kSockOptOn); - EXPECT_EQ(get_len, sizeof(get)); - - ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOff, - sizeof(kSockOptOff)), - SyscallSucceedsWithValue(0)); - - ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get, kSockOptOff); - EXPECT_EQ(get_len, sizeof(get)); -} - -// Holds TOS or TClass information for IPv4 or IPv6 respectively. -struct RecvTosOption { - int level; - int option; -}; - -RecvTosOption GetRecvTosOption(int domain) { - TEST_CHECK(domain == AF_INET || domain == AF_INET6); - RecvTosOption opt; - switch (domain) { - case AF_INET: - opt.level = IPPROTO_IP; - opt.option = IP_RECVTOS; - break; - case AF_INET6: - opt.level = IPPROTO_IPV6; - opt.option = IPV6_RECVTCLASS; - break; - } - return opt; -} - -// Ensure that Receiving TOS or TCLASS is off by default. -TEST_P(UDPSocketPairTest, RecvTosDefault) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - RecvTosOption t = GetRecvTosOption(GetParam().domain); - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -// Test that setting and getting IP_RECVTOS or IPV6_RECVTCLASS works as -// expected. -TEST_P(UDPSocketPairTest, SetRecvTos) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - RecvTosOption t = GetRecvTosOption(GetParam().domain); - - ASSERT_THAT(setsockopt(sockets->first_fd(), t.level, t.option, &kSockOptOff, - sizeof(kSockOptOff)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); - - ASSERT_THAT(setsockopt(sockets->first_fd(), t.level, t.option, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT( - getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); -} - -// Test that any socket (including IPv6 only) accepts the IPv4 TOS option: this -// mirrors behavior in linux. -TEST_P(UDPSocketPairTest, TOSRecvMismatch) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - RecvTosOption t = GetRecvTosOption(AF_INET); - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT( - getsockopt(sockets->first_fd(), t.level, t.option, &get, &get_len), - SyscallSucceedsWithValue(0)); -} - -// Test that an IPv4 socket does not support the IPv6 TClass option. -TEST_P(UDPSocketPairTest, TClassRecvMismatch) { - // This should only test AF_INET6 sockets for the mismatch behavior. - SKIP_IF(GetParam().domain != AF_INET6); - // IPV6_RECVTCLASS is only valid for SOCK_DGRAM and SOCK_RAW. - SKIP_IF((GetParam().type != SOCK_DGRAM) | (GetParam().type != SOCK_RAW)); - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT(getsockopt(sockets->first_fd(), IPPROTO_IPV6, IPV6_RECVTCLASS, - &get, &get_len), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -// Test the SO_LINGER option can be set/get on udp socket. -TEST_P(UDPSocketPairTest, SetAndGetSocketLinger) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int level = SOL_SOCKET; - int type = SO_LINGER; - - struct linger sl; - sl.l_onoff = 1; - sl.l_linger = 5; - ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &sl, sizeof(sl)), - SyscallSucceedsWithValue(0)); - - struct linger got_linger = {}; - socklen_t length = sizeof(sl); - ASSERT_THAT( - getsockopt(sockets->first_fd(), level, type, &got_linger, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&sl, &got_linger, length)); -} - -// Test getsockopt for SO_ACCEPTCONN on udp socket. -TEST_P(UDPSocketPairTest, GetSocketAcceptConn) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_generic.h b/test/syscalls/linux/socket_ip_udp_generic.h deleted file mode 100644 index 106c54e9f..000000000 --- a/test/syscalls/linux/socket_ip_udp_generic.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected UDP sockets. -using UDPSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_ diff --git a/test/syscalls/linux/socket_ip_udp_loopback.cc b/test/syscalls/linux/socket_ip_udp_loopback.cc deleted file mode 100644 index c7fa44884..000000000 --- a/test/syscalls/linux/socket_ip_udp_loopback.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_generic.h" -#include "test/syscalls/linux/socket_ip_udp_generic.h" -#include "test/syscalls/linux/socket_non_stream.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return { - IPv6UDPBidirectionalBindSocketPair(0), - IPv4UDPBidirectionalBindSocketPair(0), - DualStackUDPBidirectionalBindSocketPair(0), - }; -} - -INSTANTIATE_TEST_SUITE_P( - AllUDPSockets, AllSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - AllUDPSockets, NonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - AllUDPSockets, UDPSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc deleted file mode 100644 index d6925a8df..000000000 --- a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_non_stream_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return { - IPv6UDPBidirectionalBindSocketPair(0), - IPv4UDPBidirectionalBindSocketPair(0), - }; -} - -INSTANTIATE_TEST_SUITE_P( - BlockingUDPSockets, BlockingNonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc deleted file mode 100644 index d675eddc6..000000000 --- a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_non_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return { - IPv6UDPBidirectionalBindSocketPair(SOCK_NONBLOCK), - IPv4UDPBidirectionalBindSocketPair(SOCK_NONBLOCK), - }; -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingUDPSockets, NonBlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc deleted file mode 100644 index fdbb2216b..000000000 --- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc +++ /dev/null @@ -1,59 +0,0 @@ -// 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/syscalls/linux/socket_ip_udp_unbound_external_networking.h" - -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -void IPUDPUnboundExternalNetworkingSocketTest::SetUp() { - // FIXME(b/137899561): Linux instance for syscall tests sometimes misses its - // IPv4 address on eth0. - found_net_interfaces_ = false; - - // Get interface list. - ASSERT_NO_ERRNO(if_helper_.Load()); - std::vector<std::string> if_names = if_helper_.InterfaceList(AF_INET); - if (if_names.size() != 2) { - return; - } - - // Figure out which interface is where. - std::string lo = if_names[0]; - std::string eth = if_names[1]; - if (lo != "lo") std::swap(lo, eth); - if (lo != "lo") return; - - lo_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(lo)); - auto lo_if_addr = if_helper_.GetAddr(AF_INET, lo); - if (lo_if_addr == nullptr) { - return; - } - lo_if_addr_ = *reinterpret_cast<const sockaddr_in*>(lo_if_addr); - - eth_if_idx_ = ASSERT_NO_ERRNO_AND_VALUE(if_helper_.GetIndex(eth)); - auto eth_if_addr = if_helper_.GetAddr(AF_INET, eth); - if (eth_if_addr == nullptr) { - return; - } - eth_if_addr_ = *reinterpret_cast<const sockaddr_in*>(eth_if_addr); - - found_net_interfaces_ = true; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h deleted file mode 100644 index e5287addb..000000000 --- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h +++ /dev/null @@ -1,46 +0,0 @@ -// 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_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to unbound IP UDP sockets in a sandbox -// with external networking support. -class IPUDPUnboundExternalNetworkingSocketTest : public SimpleSocketTest { - protected: - void SetUp() override; - - IfAddrHelper if_helper_; - - // found_net_interfaces_ is set to false if SetUp() could not obtain - // all interface infos that we need. - bool found_net_interfaces_; - - // Interface infos. - int lo_if_idx_; - int eth_if_idx_; - sockaddr_in lo_if_addr_; - sockaddr_in eth_if_addr_; -}; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ip_unbound.cc b/test/syscalls/linux/socket_ip_unbound.cc deleted file mode 100644 index 029f1e872..000000000 --- a/test/syscalls/linux/socket_ip_unbound.cc +++ /dev/null @@ -1,468 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> -#include <cstring> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of IP sockets. -using IPUnboundSocketTest = SimpleSocketTest; - -TEST_P(IPUnboundSocketTest, TtlDefault) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int get = -1; - socklen_t get_sz = sizeof(get); - EXPECT_THAT(getsockopt(socket->get(), IPPROTO_IP, IP_TTL, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_TRUE(get == 64 || get == 127); - EXPECT_EQ(get_sz, sizeof(get)); -} - -TEST_P(IPUnboundSocketTest, SetTtl) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int get1 = -1; - socklen_t get1_sz = sizeof(get1); - EXPECT_THAT(getsockopt(socket->get(), IPPROTO_IP, IP_TTL, &get1, &get1_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get1_sz, sizeof(get1)); - - int set = 100; - if (set == get1) { - set += 1; - } - socklen_t set_sz = sizeof(set); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_TTL, &set, set_sz), - SyscallSucceedsWithValue(0)); - - int get2 = -1; - socklen_t get2_sz = sizeof(get2); - EXPECT_THAT(getsockopt(socket->get(), IPPROTO_IP, IP_TTL, &get2, &get2_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get2_sz, sizeof(get2)); - EXPECT_EQ(get2, set); -} - -TEST_P(IPUnboundSocketTest, ResetTtlToDefault) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int get1 = -1; - socklen_t get1_sz = sizeof(get1); - EXPECT_THAT(getsockopt(socket->get(), IPPROTO_IP, IP_TTL, &get1, &get1_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get1_sz, sizeof(get1)); - - int set1 = 100; - if (set1 == get1) { - set1 += 1; - } - socklen_t set1_sz = sizeof(set1); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_TTL, &set1, set1_sz), - SyscallSucceedsWithValue(0)); - - int set2 = -1; - socklen_t set2_sz = sizeof(set2); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_TTL, &set2, set2_sz), - SyscallSucceedsWithValue(0)); - - int get2 = -1; - socklen_t get2_sz = sizeof(get2); - EXPECT_THAT(getsockopt(socket->get(), IPPROTO_IP, IP_TTL, &get2, &get2_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get2_sz, sizeof(get2)); - EXPECT_EQ(get2, get1); -} - -TEST_P(IPUnboundSocketTest, ZeroTtl) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int set = 0; - socklen_t set_sz = sizeof(set); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_TTL, &set, set_sz), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(IPUnboundSocketTest, InvalidLargeTtl) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int set = 256; - socklen_t set_sz = sizeof(set); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_TTL, &set, set_sz), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(IPUnboundSocketTest, InvalidNegativeTtl) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int set = -2; - socklen_t set_sz = sizeof(set); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_TTL, &set, set_sz), - SyscallFailsWithErrno(EINVAL)); -} - -struct TOSOption { - int level; - int option; - int cmsg_level; -}; - -constexpr int INET_ECN_MASK = 3; - -static TOSOption GetTOSOption(int domain) { - TOSOption opt; - switch (domain) { - case AF_INET: - opt.level = IPPROTO_IP; - opt.option = IP_TOS; - opt.cmsg_level = SOL_IP; - break; - case AF_INET6: - opt.level = IPPROTO_IPV6; - opt.option = IPV6_TCLASS; - opt.cmsg_level = SOL_IPV6; - break; - } - return opt; -} - -TEST_P(IPUnboundSocketTest, TOSDefault) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - TOSOption t = GetTOSOption(GetParam().domain); - int get = -1; - socklen_t get_sz = sizeof(get); - constexpr int kDefaultTOS = 0; - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, kDefaultTOS); -} - -TEST_P(IPUnboundSocketTest, SetTOS) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = 0xC0; - socklen_t set_sz = sizeof(set); - TOSOption t = GetTOSOption(GetParam().domain); - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, set); -} - -TEST_P(IPUnboundSocketTest, ZeroTOS) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = 0; - socklen_t set_sz = sizeof(set); - TOSOption t = GetTOSOption(GetParam().domain); - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, set); -} - -TEST_P(IPUnboundSocketTest, InvalidLargeTOS) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - // Test with exceeding the byte space. - int set = 256; - constexpr int kDefaultTOS = 0; - socklen_t set_sz = sizeof(set); - TOSOption t = GetTOSOption(GetParam().domain); - if (GetParam().domain == AF_INET) { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - } else { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallFailsWithErrno(EINVAL)); - } - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, kDefaultTOS); -} - -TEST_P(IPUnboundSocketTest, CheckSkipECN) { - // Test is inconsistant on different kernels. - SKIP_IF(!IsRunningOnGvisor()); - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = 0xFF; - socklen_t set_sz = sizeof(set); - TOSOption t = GetTOSOption(GetParam().domain); - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - int expect = static_cast<uint8_t>(set); - if (GetParam().protocol == IPPROTO_TCP) { - expect &= ~INET_ECN_MASK; - } - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, expect); -} - -TEST_P(IPUnboundSocketTest, ZeroTOSOptionSize) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = 0xC0; - socklen_t set_sz = 0; - TOSOption t = GetTOSOption(GetParam().domain); - if (GetParam().domain == AF_INET) { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - } else { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallFailsWithErrno(EINVAL)); - } - int get = -1; - socklen_t get_sz = 0; - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, 0); - EXPECT_EQ(get, -1); -} - -TEST_P(IPUnboundSocketTest, SmallTOSOptionSize) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = 0xC0; - constexpr int kDefaultTOS = 0; - TOSOption t = GetTOSOption(GetParam().domain); - for (socklen_t i = 1; i < sizeof(int); i++) { - int expect_tos; - socklen_t expect_sz; - if (GetParam().domain == AF_INET) { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, i), - SyscallSucceedsWithValue(0)); - expect_tos = set; - expect_sz = sizeof(uint8_t); - } else { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, i), - SyscallFailsWithErrno(EINVAL)); - expect_tos = kDefaultTOS; - expect_sz = i; - } - uint get = -1; - socklen_t get_sz = i; - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, expect_sz); - // Account for partial copies by getsockopt, retrieve the lower - // bits specified by get_sz, while comparing against expect_tos. - EXPECT_EQ(get & ~(~0 << (get_sz * 8)), expect_tos); - } -} - -TEST_P(IPUnboundSocketTest, LargeTOSOptionSize) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = 0xC0; - TOSOption t = GetTOSOption(GetParam().domain); - for (socklen_t i = sizeof(int); i < 10; i++) { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, i), - SyscallSucceedsWithValue(0)); - int get = -1; - socklen_t get_sz = i; - // We expect the system call handler to only copy atmost sizeof(int) bytes - // as asserted by the check below. Hence, we do not expect the copy to - // overflow in getsockopt. - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(int)); - EXPECT_EQ(get, set); - } -} - -TEST_P(IPUnboundSocketTest, NegativeTOS) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int set = -1; - socklen_t set_sz = sizeof(set); - TOSOption t = GetTOSOption(GetParam().domain); - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - int expect; - if (GetParam().domain == AF_INET) { - expect = static_cast<uint8_t>(set); - if (GetParam().protocol == IPPROTO_TCP) { - expect &= ~INET_ECN_MASK; - } - } else { - // On IPv6 TCLASS, setting -1 has the effect of resetting the - // TrafficClass. - expect = 0; - } - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, expect); -} - -TEST_P(IPUnboundSocketTest, InvalidNegativeTOS) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - int set = -2; - socklen_t set_sz = sizeof(set); - TOSOption t = GetTOSOption(GetParam().domain); - int expect; - if (GetParam().domain == AF_INET) { - ASSERT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallSucceedsWithValue(0)); - expect = static_cast<uint8_t>(set); - if (GetParam().protocol == IPPROTO_TCP) { - expect &= ~INET_ECN_MASK; - } - } else { - ASSERT_THAT(setsockopt(socket->get(), t.level, t.option, &set, set_sz), - SyscallFailsWithErrno(EINVAL)); - expect = 0; - } - int get = 0; - socklen_t get_sz = sizeof(get); - ASSERT_THAT(getsockopt(socket->get(), t.level, t.option, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_sz, sizeof(get)); - EXPECT_EQ(get, expect); -} - -TEST_P(IPUnboundSocketTest, NullTOS) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - TOSOption t = GetTOSOption(GetParam().domain); - int set_sz = sizeof(int); - if (GetParam().domain == AF_INET) { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, nullptr, set_sz), - SyscallFailsWithErrno(EFAULT)); - } else { // AF_INET6 - // The AF_INET6 behavior is not yet compatible. gVisor will try to read - // optval from user memory at syscall handler, it needs substantial - // refactoring to implement this behavior just for IPv6. - if (IsRunningOnGvisor()) { - EXPECT_THAT(setsockopt(socket->get(), t.level, t.option, nullptr, set_sz), - SyscallFailsWithErrno(EFAULT)); - } else { - // Linux's IPv6 stack treats nullptr optval as input of 0, so the call - // succeeds. (net/ipv6/ipv6_sockglue.c, do_ipv6_setsockopt()) - // - // Linux's implementation would need fixing as passing a nullptr as optval - // and non-zero optlen may not be valid. - // TODO(b/158666797): Combine the gVisor and linux cases for IPv6. - // Some kernel versions return EFAULT, so we handle both. - EXPECT_THAT( - setsockopt(socket->get(), t.level, t.option, nullptr, set_sz), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(0))); - } - } - socklen_t get_sz = sizeof(int); - EXPECT_THAT(getsockopt(socket->get(), t.level, t.option, nullptr, &get_sz), - SyscallFailsWithErrno(EFAULT)); - int get = -1; - EXPECT_THAT(getsockopt(socket->get(), t.level, t.option, &get, nullptr), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_P(IPUnboundSocketTest, InsufficientBufferTOS) { - SKIP_IF(GetParam().protocol == IPPROTO_TCP); - - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - TOSOption t = GetTOSOption(GetParam().domain); - - in_addr addr4; - in6_addr addr6; - ASSERT_THAT(inet_pton(AF_INET, "127.0.0.1", &addr4), ::testing::Eq(1)); - ASSERT_THAT(inet_pton(AF_INET6, "fe80::", &addr6), ::testing::Eq(1)); - - cmsghdr cmsg = {}; - cmsg.cmsg_len = sizeof(cmsg); - cmsg.cmsg_level = t.cmsg_level; - cmsg.cmsg_type = t.option; - - msghdr msg = {}; - msg.msg_control = &cmsg; - msg.msg_controllen = sizeof(cmsg); - if (GetParam().domain == AF_INET) { - msg.msg_name = &addr4; - msg.msg_namelen = sizeof(addr4); - } else { - msg.msg_name = &addr6; - msg.msg_namelen = sizeof(addr6); - } - - EXPECT_THAT(sendmsg(socket->get(), &msg, 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(IPUnboundSocketTest, ReuseAddrDefault) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT( - getsockopt(socket->get(), SOL_SOCKET, SO_REUSEADDR, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get, kSockOptOff); - EXPECT_EQ(get_sz, sizeof(get)); -} - -TEST_P(IPUnboundSocketTest, SetReuseAddr) { - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ASSERT_THAT(setsockopt(socket->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - int get = -1; - socklen_t get_sz = sizeof(get); - ASSERT_THAT( - getsockopt(socket->get(), SOL_SOCKET, SO_REUSEADDR, &get, &get_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get, kSockOptOn); - EXPECT_EQ(get_sz, sizeof(get)); -} - -INSTANTIATE_TEST_SUITE_P( - IPUnboundSockets, IPUnboundSocketTest, - ::testing::ValuesIn(VecCat<SocketKind>( - ApplyVec<SocketKind>(IPv4UDPUnboundSocket, - std::vector<int>{0, SOCK_NONBLOCK}), - ApplyVec<SocketKind>(IPv6UDPUnboundSocket, - std::vector<int>{0, SOCK_NONBLOCK}), - ApplyVec<SocketKind>(IPv4TCPUnboundSocket, - std::vector{0, SOCK_NONBLOCK}), - ApplyVec<SocketKind>(IPv6TCPUnboundSocket, - std::vector{0, SOCK_NONBLOCK})))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ip_unbound_netlink.cc b/test/syscalls/linux/socket_ip_unbound_netlink.cc deleted file mode 100644 index 7fb1c0faf..000000000 --- a/test/syscalls/linux/socket_ip_unbound_netlink.cc +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> -#include <cstring> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_netlink_route_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of IP sockets. -using IPv6UnboundSocketTest = SimpleSocketTest; - -TEST_P(IPv6UnboundSocketTest, ConnectToBadLocalAddress_NoRandomSave) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - // TODO(gvisor.dev/issue/4595): Addresses on net devices are not saved - // across save/restore. - DisableSave ds; - - // Delete the loopback address from the loopback interface. - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET6, - /*prefixlen=*/128, &in6addr_loopback, - sizeof(in6addr_loopback))); - Cleanup defer_addr_removal = - Cleanup([loopback_link = std::move(loopback_link)] { - EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET6, - /*prefixlen=*/128, &in6addr_loopback, - sizeof(in6addr_loopback))); - }); - - TestAddress addr = V6Loopback(); - reinterpret_cast<sockaddr_in6*>(&addr.addr)->sin6_port = 65535; - auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - EXPECT_THAT(connect(sock->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -INSTANTIATE_TEST_SUITE_P(IPUnboundSockets, IPv6UnboundSocketTest, - ::testing::ValuesIn(std::vector<SocketKind>{ - IPv6UDPUnboundSocket(0), - IPv6TCPUnboundSocket(0)})); - -using IPv4UnboundSocketTest = SimpleSocketTest; - -TEST_P(IPv4UnboundSocketTest, ConnectToBadLocalAddress_NoRandomSave) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - // TODO(gvisor.dev/issue/4595): Addresses on net devices are not saved - // across save/restore. - DisableSave ds; - - // Delete the loopback address from the loopback interface. - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - struct in_addr laddr; - laddr.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/8, &laddr, sizeof(laddr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(laddr)] { - EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/8, &addr, sizeof(addr))); - }); - TestAddress addr = V4Loopback(); - reinterpret_cast<sockaddr_in*>(&addr.addr)->sin_port = 65535; - auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - EXPECT_THAT(connect(sock->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallFailsWithErrno(ENETUNREACH)); -} - -INSTANTIATE_TEST_SUITE_P(IPUnboundSockets, IPv4UnboundSocketTest, - ::testing::ValuesIn(std::vector<SocketKind>{ - IPv4UDPUnboundSocket(0), - IPv4TCPUnboundSocket(0)})); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc deleted file mode 100644 index 8eec31a46..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc +++ /dev/null @@ -1,2620 +0,0 @@ -// Copyright 2019 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/syscalls/linux/socket_ipv4_udp_unbound.h" - -#include <arpa/inet.h> -#include <net/if.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Check that packets are not received without a group membership. Default send -// interface configured by bind. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNoGroup) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the first FD to the loopback. This is an alternative to - // IP_MULTICAST_IF for setting the default send interface. - auto sender_addr = V4Loopback(); - EXPECT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address. If multicast worked like unicast, - // this would ensure that we get the packet. - auto receiver_addr = V4Any(); - EXPECT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Send the multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - EXPECT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); -} - -// Check that not setting a default send interface prevents multicast packets -// from being sent. Group membership interface configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackAddrNoDefaultSendIf) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the second FD to the v4 any address to ensure that we can receive any - // unicast packet. - auto receiver_addr = V4Any(); - EXPECT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - EXPECT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallFailsWithErrno(ENETUNREACH)); -} - -// Check that not setting a default send interface prevents multicast packets -// from being sent. Group membership interface configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNicNoDefaultSendIf) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the second FD to the v4 any address to ensure that we can receive any - // unicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - EXPECT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallFailsWithErrno(ENETUNREACH)); -} - -// Check that multicast works when the default send interface is configured by -// bind and the group membership is configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the first FD to the loopback. This is an alternative to - // IP_MULTICAST_IF for setting the default send interface. - auto sender_addr = V4Loopback(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// bind and the group membership is configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNic) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the first FD to the loopback. This is an alternative to - // IP_MULTICAST_IF for setting the default send interface. - auto sender_addr = V4Loopback(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in sendto, and the group -// membership is configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in sendto, and the group -// membership is configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreqn iface = {}; - iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in connect, and the group -// membership is configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrConnect) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto connect_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&connect_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - ASSERT_THAT( - RetryEINTR(connect)(socket1->get(), - reinterpret_cast<sockaddr*>(&connect_addr.addr), - connect_addr.addr_len), - SyscallSucceeds()); - - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(send)(socket1->get(), send_buf, sizeof(send_buf), 0), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in connect, and the group -// membership is configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreqn iface = {}; - iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto connect_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&connect_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - ASSERT_THAT( - RetryEINTR(connect)(socket1->get(), - reinterpret_cast<sockaddr*>(&connect_addr.addr), - connect_addr.addr_len), - SyscallSucceeds()); - - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(send)(socket1->get(), send_buf, sizeof(send_buf), 0), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in sendto, and the group -// membership is configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelf) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the first FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in sendto, and the group -// membership is configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreqn iface = {}; - iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the first FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in connect, and the group -// membership is configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelfConnect) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the first FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto connect_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&connect_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - EXPECT_THAT( - RetryEINTR(connect)(socket1->get(), - reinterpret_cast<sockaddr*>(&connect_addr.addr), - connect_addr.addr_len), - SyscallSucceeds()); - - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(send)(socket1->get(), send_buf, sizeof(send_buf), 0), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in connect, and the group -// membership is configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreqn iface = {}; - iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Bind the first FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto connect_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&connect_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - ASSERT_THAT( - RetryEINTR(connect)(socket1->get(), - reinterpret_cast<sockaddr*>(&connect_addr.addr), - connect_addr.addr_len), - SyscallSucceeds()); - - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(send)(socket1->get(), send_buf, sizeof(send_buf), 0), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in sendto, and the group -// membership is configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelfNoLoop) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - // Bind the first FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast works when the default send interface is configured by -// IP_MULTICAST_IF, the send address is specified in sendto, and the group -// membership is configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Set the default send interface. - ip_mreqn iface = {}; - iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that dropping a group membership that does not exist fails. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastInvalidDrop) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Unregister from a membership that we didn't have. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -// Check that dropping a group membership prevents multicast packets from being -// delivered. Default send address configured by bind and group membership -// interface configured by address. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the first FD to the loopback. This is an alternative to - // IP_MULTICAST_IF for setting the default send interface. - auto sender_addr = V4Loopback(); - EXPECT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - EXPECT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register and unregister to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - EXPECT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); -} - -// Check that dropping a group membership prevents multicast packets from being -// delivered. Default send address configured by bind and group membership -// interface configured by NIC ID. -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the first FD to the loopback. This is an alternative to - // IP_MULTICAST_IF for setting the default send interface. - auto sender_addr = V4Loopback(); - EXPECT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - EXPECT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register and unregister to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - EXPECT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfZero) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn iface = {}; - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfInvalidNic) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn iface = {}; - iface.imr_ifindex = -1; - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfInvalidAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreq iface = {}; - iface.imr_interface.s_addr = inet_addr("255.255.255"); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetShort) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Create a valid full-sized request. - ip_mreqn iface = {}; - iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - - // Send an optlen of 1 to check that optlen is enforced. - EXPECT_THAT( - setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, 1), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfDefault) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - in_addr get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - EXPECT_EQ(size, sizeof(get)); - EXPECT_EQ(get.s_addr, 0); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfDefaultReqn) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - - // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the - // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr. - // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr. - EXPECT_EQ(size, sizeof(in_addr)); - - // getsockopt(IP_MULTICAST_IF) will only return the interface address which - // hasn't been set. - EXPECT_EQ(get.imr_multiaddr.s_addr, 0); - EXPECT_EQ(get.imr_address.s_addr, 0); - EXPECT_EQ(get.imr_ifindex, 0); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetAddrGetReqn) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - in_addr set = {}; - set.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set, - sizeof(set)), - SyscallSucceeds()); - - ip_mreqn get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - - // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the - // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr. - // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr. - EXPECT_EQ(size, sizeof(in_addr)); - EXPECT_EQ(get.imr_multiaddr.s_addr, set.s_addr); - EXPECT_EQ(get.imr_address.s_addr, 0); - EXPECT_EQ(get.imr_ifindex, 0); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetReqAddrGetReqn) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreq set = {}; - set.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set, - sizeof(set)), - SyscallSucceeds()); - - ip_mreqn get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - - // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the - // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr. - // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr. - EXPECT_EQ(size, sizeof(in_addr)); - EXPECT_EQ(get.imr_multiaddr.s_addr, set.imr_interface.s_addr); - EXPECT_EQ(get.imr_address.s_addr, 0); - EXPECT_EQ(get.imr_ifindex, 0); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetNicGetReqn) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn set = {}; - set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set, - sizeof(set)), - SyscallSucceeds()); - - ip_mreqn get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - EXPECT_EQ(size, sizeof(in_addr)); - EXPECT_EQ(get.imr_multiaddr.s_addr, 0); - EXPECT_EQ(get.imr_address.s_addr, 0); - EXPECT_EQ(get.imr_ifindex, 0); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - in_addr set = {}; - set.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set, - sizeof(set)), - SyscallSucceeds()); - - in_addr get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - - EXPECT_EQ(size, sizeof(get)); - EXPECT_EQ(get.s_addr, set.s_addr); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetReqAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreq set = {}; - set.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set, - sizeof(set)), - SyscallSucceeds()); - - in_addr get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - - EXPECT_EQ(size, sizeof(get)); - EXPECT_EQ(get.s_addr, set.imr_interface.s_addr); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetNic) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn set = {}; - set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set, - sizeof(set)), - SyscallSucceeds()); - - in_addr get = {}; - socklen_t size = sizeof(get); - ASSERT_THAT( - getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size), - SyscallSucceeds()); - EXPECT_EQ(size, sizeof(get)); - EXPECT_EQ(get.s_addr, 0); -} - -TEST_P(IPv4UDPUnboundSocketTest, TestJoinGroupNoIf) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallFailsWithErrno(ENODEV)); -} - -TEST_P(IPv4UDPUnboundSocketTest, TestJoinGroupInvalidIf) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn group = {}; - group.imr_address.s_addr = inet_addr("255.255.255"); - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallFailsWithErrno(ENODEV)); -} - -// Check that multiple memberships are not allowed on the same socket. -TEST_P(IPv4UDPUnboundSocketTest, TestMultipleJoinsOnSingleSocket) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto fd = socket1->get(); - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - - EXPECT_THAT( - setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)), - SyscallSucceeds()); - - EXPECT_THAT( - setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)), - SyscallFailsWithErrno(EADDRINUSE)); -} - -// Check that two sockets can join the same multicast group at the same time. -TEST_P(IPv4UDPUnboundSocketTest, TestTwoSocketsJoinSameMulticastGroup) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Drop the membership twice on each socket, the second call for each socket - // should fail. - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallFailsWithErrno(EADDRNOTAVAIL)); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group, - sizeof(group)), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -// Check that two sockets can join the same multicast group at the same time, -// and both will receive data on it. -TEST_P(IPv4UDPUnboundSocketTest, TestMcastReceptionOnTwoSockets) { - std::unique_ptr<SocketPair> socket_pairs[2] = { - absl::make_unique<FDSocketPair>(ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket())), - absl::make_unique<FDSocketPair>(ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket()))}; - - ip_mreq iface = {}, group = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - auto receiver_addr = V4Any(); - int bound_port = 0; - - // Create two socketpairs with the exact same configuration. - for (auto& sockets : socket_pairs) { - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, - &iface, sizeof(iface)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &group, sizeof(group)), - SyscallSucceeds()); - ASSERT_THAT(bind(sockets->second_fd(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - // Get the port assigned. - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(sockets->second_fd(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - // On the first iteration, save the port we are bound to. On the second - // iteration, verify the port is the same as the one from the first - // iteration. In other words, both sockets listen on the same port. - if (bound_port == 0) { - bound_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - } else { - EXPECT_EQ(bound_port, - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port); - } - } - - // Send a multicast packet to the group from two different sockets and verify - // it is received by both sockets that joined that group. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = bound_port; - for (auto& sockets : socket_pairs) { - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT( - RetryEINTR(sendto)(sockets->first_fd(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet on both sockets. - for (auto& sockets : socket_pairs) { - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RecvTimeout(sockets->second_fd(), recv_buf, sizeof(recv_buf), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - } - } -} - -// Check that on two sockets that joined a group and listen on ANY, dropping -// memberships one by one will continue to deliver packets to both sockets until -// both memberships have been dropped. -TEST_P(IPv4UDPUnboundSocketTest, TestMcastReceptionWhenDroppingMemberships) { - std::unique_ptr<SocketPair> socket_pairs[2] = { - absl::make_unique<FDSocketPair>(ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket())), - absl::make_unique<FDSocketPair>(ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket()))}; - - ip_mreq iface = {}, group = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - auto receiver_addr = V4Any(); - int bound_port = 0; - - // Create two socketpairs with the exact same configuration. - for (auto& sockets : socket_pairs) { - ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_MULTICAST_IF, - &iface, sizeof(iface)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(sockets->second_fd(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &group, sizeof(group)), - SyscallSucceeds()); - ASSERT_THAT(bind(sockets->second_fd(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - // Get the port assigned. - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(sockets->second_fd(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - // On the first iteration, save the port we are bound to. On the second - // iteration, verify the port is the same as the one from the first - // iteration. In other words, both sockets listen on the same port. - if (bound_port == 0) { - bound_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - } else { - EXPECT_EQ(bound_port, - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port); - } - } - - // Drop the membership of the first socket pair and verify data is still - // received. - ASSERT_THAT(setsockopt(socket_pairs[0]->second_fd(), IPPROTO_IP, - IP_DROP_MEMBERSHIP, &group, sizeof(group)), - SyscallSucceeds()); - // Send a packet from each socket_pair. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = bound_port; - for (auto& sockets : socket_pairs) { - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT( - RetryEINTR(sendto)(sockets->first_fd(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet on both sockets. - for (auto& sockets : socket_pairs) { - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RecvTimeout(sockets->second_fd(), recv_buf, sizeof(recv_buf), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - } - } - - // Drop the membership of the second socket pair and verify data stops being - // received. - ASSERT_THAT(setsockopt(socket_pairs[1]->second_fd(), IPPROTO_IP, - IP_DROP_MEMBERSHIP, &group, sizeof(group)), - SyscallSucceeds()); - // Send a packet from each socket_pair. - for (auto& sockets : socket_pairs) { - char send_buf[200]; - ASSERT_THAT( - RetryEINTR(sendto)(sockets->first_fd(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - char recv_buf[sizeof(send_buf)] = {}; - for (auto& sockets : socket_pairs) { - ASSERT_THAT(RecvTimeout(sockets->second_fd(), recv_buf, sizeof(recv_buf), - 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); - } - } -} - -// Check that a receiving socket can bind to the multicast address before -// joining the group and receive data once the group has been joined. -TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenJoinThenReceive) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind second socket (receiver) to the multicast address. - auto receiver_addr = V4Multicast(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - // Update receiver_addr with the correct port number. - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet on the first socket out the loopback interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - auto sendto_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that a receiving socket can bind to the multicast address and won't -// receive multicast data if it hasn't joined the group. -TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenNoJoinThenNoReceive) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind second socket (receiver) to the multicast address. - auto receiver_addr = V4Multicast(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - // Update receiver_addr with the correct port number. - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Send a multicast packet on the first socket out the loopback interface. - ip_mreq iface = {}; - iface.imr_interface.s_addr = htonl(INADDR_LOOPBACK); - ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - auto sendto_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we don't receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); -} - -// Check that a socket can bind to a multicast address and still send out -// packets. -TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenSend) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind second socket (receiver) to the ANY address. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Bind the first socket (sender) to the multicast address. - auto sender_addr = V4Multicast(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - socklen_t sender_addr_len = sender_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&sender_addr.addr), - &sender_addr_len), - SyscallSucceeds()); - EXPECT_EQ(sender_addr_len, sender_addr.addr_len); - - // Send a packet on the first socket to the loopback address. - auto sendto_addr = V4Loopback(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that a receiving socket can bind to the broadcast address and receive -// broadcast packets. -TEST_P(IPv4UDPUnboundSocketTest, TestBindToBcastThenReceive) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind second socket (receiver) to the broadcast address. - auto receiver_addr = V4Broadcast(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Send a broadcast packet on the first socket out the loopback interface. - EXPECT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - // Note: Binding to the loopback interface makes the broadcast go out of it. - auto sender_bind_addr = V4Loopback(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_bind_addr.addr), - sender_bind_addr.addr_len), - SyscallSucceeds()); - auto sendto_addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that a socket can bind to the broadcast address and still send out -// packets. -TEST_P(IPv4UDPUnboundSocketTest, TestBindToBcastThenSend) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind second socket (receiver) to the ANY address. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(socket2->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(socket2->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Bind the first socket (sender) to the broadcast address. - auto sender_addr = V4Broadcast(); - ASSERT_THAT( - bind(socket1->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - socklen_t sender_addr_len = sender_addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&sender_addr.addr), - &sender_addr_len), - SyscallSucceeds()); - EXPECT_EQ(sender_addr_len, sender_addr.addr_len); - - // Send a packet on the first socket to the loopback address. - auto sendto_addr = V4Loopback(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket1->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that SO_REUSEADDR always delivers to the most recently bound socket. -// -// FIXME(gvisor.dev/issue/873): Endpoint order is not restored correctly. Enable -// random and co-op save (below) once that is fixed. -TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrDistribution_NoRandomSave) { - std::vector<std::unique_ptr<FileDescriptor>> sockets; - sockets.emplace_back(ASSERT_NO_ERRNO_AND_VALUE(NewSocket())); - - ASSERT_THAT(setsockopt(sockets[0]->get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(sockets[0]->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(sockets[0]->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - constexpr int kMessageSize = 200; - - // FIXME(gvisor.dev/issue/873): Endpoint order is not restored correctly. - const DisableSave ds; - - for (int i = 0; i < 10; i++) { - // Add a new receiver. - sockets.emplace_back(ASSERT_NO_ERRNO_AND_VALUE(NewSocket())); - auto& last = sockets.back(); - ASSERT_THAT(setsockopt(last->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(last->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Send a new message to the SO_REUSEADDR group. We use a new socket each - // time so that a new ephemeral port will be used each time. This ensures - // that we aren't doing REUSEPORT-like hash load blancing. - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - char send_buf[kMessageSize]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - EXPECT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Verify that the most recent socket got the message. We don't expect any - // of the other sockets to have received it, but we will check that later. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RecvTimeout(last->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(send_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - } - - // Verify that no other messages were received. - for (auto& socket : sockets) { - char recv_buf[kMessageSize] = {}; - EXPECT_THAT( - RecvTimeout(socket->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); - } -} - -TEST_P(IPv4UDPUnboundSocketTest, BindReuseAddrThenReusePort) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, only with REUSEPORT. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindReusePortThenReuseAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, only with REUSEADDR. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindReuseAddrReusePortConvertibleToReusePort) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket3 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR and REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, only with REUSEPORT. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Bind socket3 to the same address as socket1, only with REUSEADDR. - ASSERT_THAT(setsockopt(socket3->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket3->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindReuseAddrReusePortConvertibleToReuseAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket3 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR and REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, only with REUSEADDR. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Bind socket3 to the same address as socket1, only with REUSEPORT. - ASSERT_THAT(setsockopt(socket3->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket3->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindReuseAddrReusePortConversionReversable1) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket3 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR and REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, only with REUSEPORT. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Close socket2 to revert to just socket1 with REUSEADDR and REUSEPORT. - socket2->reset(); - - // Bind socket3 to the same address as socket1, only with REUSEADDR. - ASSERT_THAT(setsockopt(socket3->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket3->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindReuseAddrReusePortConversionReversable2) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket3 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR and REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, only with REUSEADDR. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Close socket2 to revert to just socket1 with REUSEADDR and REUSEPORT. - socket2->reset(); - - // Bind socket3 to the same address as socket1, only with REUSEPORT. - ASSERT_THAT(setsockopt(socket3->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket3->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindDoubleReuseAddrReusePortThenReusePort) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket3 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR and REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, also with REUSEADDR and - // REUSEPORT. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Bind socket3 to the same address as socket1, only with REUSEPORT. - ASSERT_THAT(setsockopt(socket3->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket3->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); -} - -TEST_P(IPv4UDPUnboundSocketTest, BindDoubleReuseAddrReusePortThenReuseAddr) { - auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto socket3 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind socket1 with REUSEADDR and REUSEPORT. - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(socket1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(socket1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind socket2 to the same address as socket1, also with REUSEADDR and - // REUSEPORT. - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(socket2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - ASSERT_THAT(bind(socket2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - // Bind socket3 to the same address as socket1, only with REUSEADDR. - ASSERT_THAT(setsockopt(socket3->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(socket3->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); -} - -// Check that REUSEPORT takes precedence over REUSEADDR. -TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrReusePortDistribution) { - auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - ASSERT_THAT(setsockopt(receiver1->get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(receiver1->get(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(receiver1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(receiver1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Bind receiver2 to the same address as socket1, also with REUSEADDR and - // REUSEPORT. - ASSERT_THAT(setsockopt(receiver2->get(), SOL_SOCKET, SO_REUSEADDR, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(setsockopt(receiver2->get(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT(bind(receiver2->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - - constexpr int kMessageSize = 10; - - // Saving during each iteration of the following loop is too expensive. - DisableSave ds; - - for (int i = 0; i < 100; ++i) { - // Send a new message to the REUSEADDR/REUSEPORT group. We use a new socket - // each time so that a new ephemerial port will be used each time. This - // ensures that we cycle through hashes. - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - char send_buf[kMessageSize] = {}; - EXPECT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - } - - ds.reset(); - - // Check that both receivers got messages. This checks that we are using load - // balancing (REUSEPORT) instead of the most recently bound socket - // (REUSEADDR). - char recv_buf[kMessageSize] = {}; - EXPECT_THAT( - RecvTimeout(receiver1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(kMessageSize)); - EXPECT_THAT( - RecvTimeout(receiver2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), - IsPosixErrorOkAndHolds(kMessageSize)); -} - -// Test that socket will receive packet info control message. -TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) { - // TODO(gvisor.dev/issue/1202): ioctl() is not supported by hostinet. - SKIP_IF((IsRunningWithHostinet())); - - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto sender_addr = V4Loopback(); - int level = SOL_IP; - int type = IP_PKTINFO; - - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - socklen_t sender_addr_len = sender_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&sender_addr.addr), - &sender_addr_len), - SyscallSucceeds()); - EXPECT_EQ(sender_addr_len, sender_addr.addr_len); - - auto receiver_addr = V4Loopback(); - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&sender_addr.addr)->sin_port; - ASSERT_THAT( - connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - - // Allow socket to receive control message. - ASSERT_THAT( - setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Prepare message to send. - constexpr size_t kDataLength = 1024; - msghdr sent_msg = {}; - iovec sent_iov = {}; - char sent_data[kDataLength]; - sent_iov.iov_base = sent_data; - sent_iov.iov_len = kDataLength; - sent_msg.msg_iov = &sent_iov; - sent_msg.msg_iovlen = 1; - sent_msg.msg_flags = 0; - - ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - msghdr received_msg = {}; - iovec received_iov = {}; - char received_data[kDataLength]; - char received_cmsg_buf[CMSG_SPACE(sizeof(in_pktinfo))] = {}; - size_t cmsg_data_len = sizeof(in_pktinfo); - received_iov.iov_base = received_data; - received_iov.iov_len = kDataLength; - received_msg.msg_iov = &received_iov; - received_msg.msg_iovlen = 1; - received_msg.msg_controllen = CMSG_LEN(cmsg_data_len); - received_msg.msg_control = received_cmsg_buf; - - ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/), - IsPosixErrorOkAndHolds(kDataLength)); - - cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len)); - EXPECT_EQ(cmsg->cmsg_level, level); - EXPECT_EQ(cmsg->cmsg_type, type); - - // Get loopback index. - ifreq ifr = {}; - absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo"); - ASSERT_THAT(ioctl(sender->get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); - ASSERT_NE(ifr.ifr_ifindex, 0); - - // Check the data - in_pktinfo received_pktinfo = {}; - memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo)); - EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex); - EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, htonl(INADDR_LOOPBACK)); - EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK)); -} - -// Test that socket will receive IP_RECVORIGDSTADDR control message. -TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) { - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver_addr = V4Loopback(); - int level = SOL_IP; - int type = IP_RECVORIGDSTADDR; - - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - - // Retrieve the port bound by the receiver. - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - ASSERT_THAT( - connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - - // Get address and port bound by the sender. - sockaddr_storage sender_addr_storage; - socklen_t sender_addr_len = sizeof(sender_addr_storage); - ASSERT_THAT(getsockname(sender->get(), - reinterpret_cast<sockaddr*>(&sender_addr_storage), - &sender_addr_len), - SyscallSucceeds()); - ASSERT_EQ(sender_addr_len, sizeof(struct sockaddr_in)); - - // Enable IP_RECVORIGDSTADDR on socket so that we get the original destination - // address of the datagram as auxiliary information in the control message. - ASSERT_THAT( - setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Prepare message to send. - constexpr size_t kDataLength = 1024; - msghdr sent_msg = {}; - iovec sent_iov = {}; - char sent_data[kDataLength]; - sent_iov.iov_base = sent_data; - sent_iov.iov_len = kDataLength; - sent_msg.msg_iov = &sent_iov; - sent_msg.msg_iovlen = 1; - sent_msg.msg_flags = 0; - - ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - msghdr received_msg = {}; - iovec received_iov = {}; - char received_data[kDataLength]; - char received_cmsg_buf[CMSG_SPACE(sizeof(sockaddr_in))] = {}; - size_t cmsg_data_len = sizeof(sockaddr_in); - received_iov.iov_base = received_data; - received_iov.iov_len = kDataLength; - received_msg.msg_iov = &received_iov; - received_msg.msg_iovlen = 1; - received_msg.msg_controllen = CMSG_LEN(cmsg_data_len); - received_msg.msg_control = received_cmsg_buf; - - ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/), - IsPosixErrorOkAndHolds(kDataLength)); - - cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len)); - EXPECT_EQ(cmsg->cmsg_level, level); - EXPECT_EQ(cmsg->cmsg_type, type); - - // Check the data - sockaddr_in received_addr = {}; - memcpy(&received_addr, CMSG_DATA(cmsg), sizeof(received_addr)); - auto orig_receiver_addr = reinterpret_cast<sockaddr_in*>(&receiver_addr.addr); - EXPECT_EQ(received_addr.sin_addr.s_addr, orig_receiver_addr->sin_addr.s_addr); - EXPECT_EQ(received_addr.sin_port, orig_receiver_addr->sin_port); -} - -// Check that setting SO_RCVBUF below min is clamped to the minimum -// receive buffer size. -TEST_P(IPv4UDPUnboundSocketTest, SetSocketRecvBufBelowMin) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Discover minimum buffer size by setting it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, - sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - - // Linux doubles the value so let's use a value that when doubled will still - // be smaller than min. - int below_min = min / 2 - 1; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &below_min, - sizeof(below_min)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - - ASSERT_EQ(min, val); -} - -// Check that setting SO_RCVBUF above max is clamped to the maximum -// receive buffer size. -TEST_P(IPv4UDPUnboundSocketTest, SetSocketRecvBufAboveMax) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Discover maxmimum buffer size by setting to a really large value. - constexpr int kRcvBufSz = 0xffffffff; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, - sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &max, &max_len), - SyscallSucceeds()); - - int above_max = max + 1; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &above_max, - sizeof(above_max)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(max, val); -} - -// Check that setting SO_RCVBUF min <= rcvBufSz <= max is honored. -TEST_P(IPv4UDPUnboundSocketTest, SetSocketRecvBuf) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int max = 0; - int min = 0; - { - // Discover maxmimum buffer size by setting to a really large value. - constexpr int kRcvBufSz = 0xffffffff; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, - sizeof(kRcvBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by setting it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, - sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &quarter_sz, - sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_RCVBUF, &val, &val_len), - SyscallSucceeds()); - - // Linux doubles the value set by SO_SNDBUF/SO_RCVBUF. - if (!IsRunningOnGvisor()) { - quarter_sz *= 2; - } - ASSERT_EQ(quarter_sz, val); -} - -// Check that setting SO_SNDBUF below min is clamped to the minimum -// send buffer size. -TEST_P(IPv4UDPUnboundSocketTest, SetSocketSendBufBelowMin) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Discover minimum buffer size by setting it to zero. - constexpr int kSndBufSz = 0; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &kSndBufSz, - sizeof(kSndBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - - // Linux doubles the value so let's use a value that when doubled will still - // be smaller than min. - int below_min = min / 2 - 1; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &below_min, - sizeof(below_min)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - ASSERT_EQ(min, val); -} - -// Check that setting SO_SNDBUF above max is clamped to the maximum -// send buffer size. -TEST_P(IPv4UDPUnboundSocketTest, SetSocketSendBufAboveMax) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Discover maxmimum buffer size by setting to a really large value. - constexpr int kSndBufSz = 0xffffffff; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &kSndBufSz, - sizeof(kSndBufSz)), - SyscallSucceeds()); - - int max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - - int above_max = max + 1; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &above_max, - sizeof(above_max)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - ASSERT_EQ(max, val); -} - -// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored. -TEST_P(IPv4UDPUnboundSocketTest, SetSocketSendBuf) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int max = 0; - int min = 0; - { - // Discover maxmimum buffer size by setting to a really large value. - constexpr int kSndBufSz = 0xffffffff; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &kSndBufSz, - sizeof(kSndBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by setting it to zero. - constexpr int kSndBufSz = 0; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &kSndBufSz, - sizeof(kSndBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT(setsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &quarter_sz, - sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - quarter_sz *= 2; - ASSERT_EQ(quarter_sz, val); -} - -TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIPPacketInfo) { - auto sender_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver_socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the first FD to the loopback. This is an alternative to - // IP_MULTICAST_IF for setting the default send interface. - auto sender_addr = V4Loopback(); - ASSERT_THAT( - bind(sender_socket->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT(bind(receiver_socket->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver_socket->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")); - ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &group, sizeof(group)), - SyscallSucceeds()); - - // Register to receive IP packet info. - const int one = 1; - ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IP, IP_PKTINFO, &one, - sizeof(one)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT( - RetryEINTR(sendto)(sender_socket->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - msghdr recv_msg = {}; - iovec recv_iov = {}; - char recv_buf[sizeof(send_buf)]; - char recv_cmsg_buf[CMSG_SPACE(sizeof(in_pktinfo))] = {}; - size_t cmsg_data_len = sizeof(in_pktinfo); - recv_iov.iov_base = recv_buf; - recv_iov.iov_len = sizeof(recv_buf); - recv_msg.msg_iov = &recv_iov; - recv_msg.msg_iovlen = 1; - recv_msg.msg_controllen = CMSG_LEN(cmsg_data_len); - recv_msg.msg_control = recv_cmsg_buf; - ASSERT_THAT(RetryEINTR(recvmsg)(receiver_socket->get(), &recv_msg, 0), - SyscallSucceedsWithValue(sizeof(send_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - - // Check the IP_PKTINFO control message. - cmsghdr* cmsg = CMSG_FIRSTHDR(&recv_msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len)); - EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IP); - EXPECT_EQ(cmsg->cmsg_type, IP_PKTINFO); - - // Get loopback index. - ifreq ifr = {}; - absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo"); - ASSERT_THAT(ioctl(receiver_socket->get(), SIOCGIFINDEX, &ifr), - SyscallSucceeds()); - ASSERT_NE(ifr.ifr_ifindex, 0); - - in_pktinfo received_pktinfo = {}; - memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo)); - EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex); - if (IsRunningOnGvisor()) { - // This should actually be a unicast address assigned to the interface. - // - // TODO(gvisor.dev/issue/3556): This check is validating incorrect - // behaviour. We still include the test so that once the bug is - // resolved, this test will start to fail and the individual tasked - // with fixing this bug knows to also fix this test :). - EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, group.imr_multiaddr.s_addr); - } else { - EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, htonl(INADDR_LOOPBACK)); - } - EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, group.imr_multiaddr.s_addr); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.h b/test/syscalls/linux/socket_ipv4_udp_unbound.h deleted file mode 100644 index f64c57645..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to IPv4 UDP sockets. -using IPv4UDPUnboundSocketTest = SimpleSocketTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_ diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc deleted file mode 100644 index 940289d15..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.cc +++ /dev/null @@ -1,1030 +0,0 @@ -// Copyright 2019 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/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h" - -namespace gvisor { -namespace testing { - -TestAddress V4EmptyAddress() { - TestAddress t("V4Empty"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - return t; -} - -// Verifies that a broadcast UDP packet will arrive at all UDP sockets with -// the destination port number. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - UDPBroadcastReceivedOnExpectedPort) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto norcv = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Enable SO_BROADCAST on the sending socket. - ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - // Enable SO_REUSEPORT on the receiving sockets so that they may both be bound - // to the broadcast messages destination port. - ASSERT_THAT(setsockopt(rcvr1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(setsockopt(rcvr2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - // Bind the first socket to the ANY address and let the system assign a port. - auto rcv1_addr = V4Any(); - ASSERT_THAT(bind(rcvr1->get(), reinterpret_cast<sockaddr*>(&rcv1_addr.addr), - rcv1_addr.addr_len), - SyscallSucceedsWithValue(0)); - // Retrieve port number from first socket so that it can be bound to the - // second socket. - socklen_t rcv_addr_sz = rcv1_addr.addr_len; - ASSERT_THAT( - getsockname(rcvr1->get(), reinterpret_cast<sockaddr*>(&rcv1_addr.addr), - &rcv_addr_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(rcv_addr_sz, rcv1_addr.addr_len); - auto port = reinterpret_cast<sockaddr_in*>(&rcv1_addr.addr)->sin_port; - - // Bind the second socket to the same address:port as the first. - ASSERT_THAT(bind(rcvr2->get(), reinterpret_cast<sockaddr*>(&rcv1_addr.addr), - rcv_addr_sz), - SyscallSucceedsWithValue(0)); - - // Bind the non-receiving socket to an ephemeral port. - auto norecv_addr = V4Any(); - ASSERT_THAT(bind(norcv->get(), reinterpret_cast<sockaddr*>(&norecv_addr.addr), - norecv_addr.addr_len), - SyscallSucceedsWithValue(0)); - - // Broadcast a test message. - auto dst_addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&dst_addr.addr)->sin_port = port; - constexpr char kTestMsg[] = "hello, world"; - EXPECT_THAT( - sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, - reinterpret_cast<sockaddr*>(&dst_addr.addr), dst_addr.addr_len), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - - // Verify that the receiving sockets received the test message. - char buf[sizeof(kTestMsg)] = {}; - EXPECT_THAT(recv(rcvr1->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); - memset(buf, 0, sizeof(buf)); - EXPECT_THAT(recv(rcvr2->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); - - // Verify that the non-receiving socket did not receive the test message. - memset(buf, 0, sizeof(buf)); - EXPECT_THAT(RetryEINTR(recv)(norcv->get(), buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Verifies that a broadcast UDP packet will arrive at all UDP sockets bound to -// the destination port number and either INADDR_ANY or INADDR_BROADCAST, but -// not a unicast address. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - UDPBroadcastReceivedOnExpectedAddresses) { - SKIP_IF(!found_net_interfaces_); - - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto rcvr1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto rcvr2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto norcv = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Enable SO_BROADCAST on the sending socket. - ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - // Enable SO_REUSEPORT on all sockets so that they may all be bound to the - // broadcast messages destination port. - ASSERT_THAT(setsockopt(rcvr1->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(setsockopt(rcvr2->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - ASSERT_THAT(setsockopt(norcv->get(), SOL_SOCKET, SO_REUSEPORT, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - // Bind the first socket the ANY address and let the system assign a port. - auto rcv1_addr = V4Any(); - ASSERT_THAT(bind(rcvr1->get(), reinterpret_cast<sockaddr*>(&rcv1_addr.addr), - rcv1_addr.addr_len), - SyscallSucceedsWithValue(0)); - // Retrieve port number from first socket so that it can be bound to the - // second socket. - socklen_t rcv_addr_sz = rcv1_addr.addr_len; - ASSERT_THAT( - getsockname(rcvr1->get(), reinterpret_cast<sockaddr*>(&rcv1_addr.addr), - &rcv_addr_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(rcv_addr_sz, rcv1_addr.addr_len); - auto port = reinterpret_cast<sockaddr_in*>(&rcv1_addr.addr)->sin_port; - - // Bind the second socket to the broadcast address. - auto rcv2_addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&rcv2_addr.addr)->sin_port = port; - ASSERT_THAT(bind(rcvr2->get(), reinterpret_cast<sockaddr*>(&rcv2_addr.addr), - rcv2_addr.addr_len), - SyscallSucceedsWithValue(0)); - - // Bind the non-receiving socket to the unicast ethernet address. - auto norecv_addr = rcv1_addr; - reinterpret_cast<sockaddr_in*>(&norecv_addr.addr)->sin_addr = - eth_if_addr_.sin_addr; - ASSERT_THAT(bind(norcv->get(), reinterpret_cast<sockaddr*>(&norecv_addr.addr), - norecv_addr.addr_len), - SyscallSucceedsWithValue(0)); - - // Broadcast a test message. - auto dst_addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&dst_addr.addr)->sin_port = port; - constexpr char kTestMsg[] = "hello, world"; - EXPECT_THAT( - sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, - reinterpret_cast<sockaddr*>(&dst_addr.addr), dst_addr.addr_len), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - - // Verify that the receiving sockets received the test message. - char buf[sizeof(kTestMsg)] = {}; - EXPECT_THAT(recv(rcvr1->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); - memset(buf, 0, sizeof(buf)); - EXPECT_THAT(recv(rcvr2->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); - - // Verify that the non-receiving socket did not receive the test message. - memset(buf, 0, sizeof(buf)); - EXPECT_THAT(RetryEINTR(recv)(norcv->get(), buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Verifies that a UDP broadcast can be sent and then received back on the same -// socket that is bound to the broadcast address (255.255.255.255). -// FIXME(b/141938460): This can be combined with the next test -// (UDPBroadcastSendRecvOnSocketBoundToAny). -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - UDPBroadcastSendRecvOnSocketBoundToBroadcast) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Enable SO_BROADCAST. - ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - // Bind the sender to the broadcast address. - auto src_addr = V4Broadcast(); - ASSERT_THAT(bind(sender->get(), reinterpret_cast<sockaddr*>(&src_addr.addr), - src_addr.addr_len), - SyscallSucceedsWithValue(0)); - socklen_t src_sz = src_addr.addr_len; - ASSERT_THAT(getsockname(sender->get(), - reinterpret_cast<sockaddr*>(&src_addr.addr), &src_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(src_sz, src_addr.addr_len); - - // Send the message. - auto dst_addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&dst_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&src_addr.addr)->sin_port; - constexpr char kTestMsg[] = "hello, world"; - EXPECT_THAT( - sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, - reinterpret_cast<sockaddr*>(&dst_addr.addr), dst_addr.addr_len), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - - // Verify that the message was received. - char buf[sizeof(kTestMsg)] = {}; - EXPECT_THAT(RetryEINTR(recv)(sender->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); -} - -// Verifies that a UDP broadcast can be sent and then received back on the same -// socket that is bound to the ANY address (0.0.0.0). -// FIXME(b/141938460): This can be combined with the previous test -// (UDPBroadcastSendRecvOnSocketBoundToBroadcast). -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - UDPBroadcastSendRecvOnSocketBoundToAny) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Enable SO_BROADCAST. - ASSERT_THAT(setsockopt(sender->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)); - - // Bind the sender to the ANY address. - auto src_addr = V4Any(); - ASSERT_THAT(bind(sender->get(), reinterpret_cast<sockaddr*>(&src_addr.addr), - src_addr.addr_len), - SyscallSucceedsWithValue(0)); - socklen_t src_sz = src_addr.addr_len; - ASSERT_THAT(getsockname(sender->get(), - reinterpret_cast<sockaddr*>(&src_addr.addr), &src_sz), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(src_sz, src_addr.addr_len); - - // Send the message. - auto dst_addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&dst_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&src_addr.addr)->sin_port; - constexpr char kTestMsg[] = "hello, world"; - EXPECT_THAT( - sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, - reinterpret_cast<sockaddr*>(&dst_addr.addr), dst_addr.addr_len), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - - // Verify that the message was received. - char buf[sizeof(kTestMsg)] = {}; - EXPECT_THAT(RetryEINTR(recv)(sender->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - EXPECT_EQ(0, memcmp(buf, kTestMsg, sizeof(kTestMsg))); -} - -// Verifies that a UDP broadcast fails to send on a socket with SO_BROADCAST -// disabled. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendBroadcast) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Broadcast a test message without having enabled SO_BROADCAST on the sending - // socket. - auto addr = V4Broadcast(); - reinterpret_cast<sockaddr_in*>(&addr.addr)->sin_port = htons(12345); - constexpr char kTestMsg[] = "hello, world"; - - EXPECT_THAT(sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, - reinterpret_cast<sockaddr*>(&addr.addr), addr.addr_len), - SyscallFailsWithErrno(EACCES)); -} - -// Verifies that a UDP unicast on an unbound socket reaches its destination. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendUnicastOnUnbound) { - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto rcvr = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the receiver and retrieve its address and port number. - sockaddr_in addr = {}; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_ANY); - addr.sin_port = htons(0); - ASSERT_THAT(bind(rcvr->get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceedsWithValue(0)); - memset(&addr, 0, sizeof(addr)); - socklen_t addr_sz = sizeof(addr); - ASSERT_THAT(getsockname(rcvr->get(), - reinterpret_cast<struct sockaddr*>(&addr), &addr_sz), - SyscallSucceedsWithValue(0)); - - // Send a test message to the receiver. - constexpr char kTestMsg[] = "hello, world"; - ASSERT_THAT(sendto(sender->get(), kTestMsg, sizeof(kTestMsg), 0, - reinterpret_cast<struct sockaddr*>(&addr), addr_sz), - SyscallSucceedsWithValue(sizeof(kTestMsg))); - char buf[sizeof(kTestMsg)] = {}; - ASSERT_THAT(recv(rcvr->get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(kTestMsg))); -} - -// Check that multicast packets won't be delivered to the sending socket with no -// set interface or group membership. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastSelfNoGroup) { - // FIXME(b/125485338): A group membership is not required for external - // multicast on gVisor. - SKIP_IF(IsRunningOnGvisor()); - - SKIP_IF(!found_net_interfaces_); - - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - auto bind_addr = V4Any(); - ASSERT_THAT(bind(socket->get(), reinterpret_cast<sockaddr*>(&bind_addr.addr), - bind_addr.addr_len), - SyscallSucceeds()); - socklen_t bind_addr_len = bind_addr.addr_len; - ASSERT_THAT( - getsockname(socket->get(), reinterpret_cast<sockaddr*>(&bind_addr.addr), - &bind_addr_len), - SyscallSucceeds()); - EXPECT_EQ(bind_addr_len, bind_addr.addr_len); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&bind_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RetryEINTR(recv)(socket->get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Check that multicast packets will be delivered to the sending socket without -// setting an interface. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastSelf) { - SKIP_IF(!found_net_interfaces_); - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - auto bind_addr = V4Any(); - ASSERT_THAT(bind(socket->get(), reinterpret_cast<sockaddr*>(&bind_addr.addr), - bind_addr.addr_len), - SyscallSucceeds()); - socklen_t bind_addr_len = bind_addr.addr_len; - ASSERT_THAT( - getsockname(socket->get(), reinterpret_cast<sockaddr*>(&bind_addr.addr), - &bind_addr_len), - SyscallSucceeds()); - EXPECT_EQ(bind_addr_len, bind_addr.addr_len); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - ASSERT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&bind_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RetryEINTR(recv)(socket->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast packets won't be delivered to the sending socket with no -// set interface and IP_MULTICAST_LOOP disabled. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastSelfLoopOff) { - SKIP_IF(!found_net_interfaces_); - auto socket = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - auto bind_addr = V4Any(); - ASSERT_THAT(bind(socket->get(), reinterpret_cast<sockaddr*>(&bind_addr.addr), - bind_addr.addr_len), - SyscallSucceeds()); - socklen_t bind_addr_len = bind_addr.addr_len; - ASSERT_THAT( - getsockname(socket->get(), reinterpret_cast<sockaddr*>(&bind_addr.addr), - &bind_addr_len), - SyscallSucceeds()); - EXPECT_EQ(bind_addr_len, bind_addr.addr_len); - - // Disable multicast looping. - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - // Register to receive multicast packets. - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - EXPECT_THAT(setsockopt(socket->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&bind_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(socket->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - EXPECT_THAT( - RetryEINTR(recv)(socket->get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Check that multicast packets won't be delivered to another socket with no -// set interface or group membership. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticastNoGroup) { - // FIXME(b/125485338): A group membership is not required for external - // multicast on gVisor. - SKIP_IF(IsRunningOnGvisor()); - - SKIP_IF(!found_net_interfaces_); - - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), - MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Check that multicast packets will be delivered to another socket without -// setting an interface. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, TestSendMulticast) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that multicast packets won't be delivered to another socket with no -// set interface and IP_MULTICAST_LOOP disabled on the sending socket. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastSenderNoLoop) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Disable multicast looping on the sender. - EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - EXPECT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we did not receive the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), - MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Check that multicast packets will be delivered to the sending socket without -// setting an interface and IP_MULTICAST_LOOP disabled on the receiving socket. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastReceiverNoLoop) { - SKIP_IF(!found_net_interfaces_); - - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Bind the second FD to the v4 any address to ensure that we can receive the - // multicast packet. - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Disable multicast looping on the receiver. - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_MULTICAST_LOOP, - &kSockOptOff, sizeof(kSockOptOff)), - SyscallSucceeds()); - - // Register to receive multicast packets. - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); -} - -// Check that two sockets can join the same multicast group at the same time, -// and both will receive data on it when bound to the ANY address. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastToTwoBoundToAny) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - std::unique_ptr<FileDescriptor> receivers[2] = { - ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket())}; - - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - auto receiver_addr = V4Any(); - int bound_port = 0; - for (auto& receiver : receivers) { - ASSERT_THAT(setsockopt(receiver->get(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - // Bind to ANY to receive multicast packets. - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - EXPECT_EQ( - htonl(INADDR_ANY), - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_addr.s_addr); - // On the first iteration, save the port we are bound to. On the second - // iteration, verify the port is the same as the one from the first - // iteration. In other words, both sockets listen on the same port. - if (bound_port == 0) { - bound_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - } else { - EXPECT_EQ(bound_port, - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port); - } - - // Register to receive multicast packets. - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &group, sizeof(group)), - SyscallSucceeds()); - } - - // Send a multicast packet to the group and verify both receivers get it. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = bound_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - for (auto& receiver : receivers) { - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - } -} - -// Check that two sockets can join the same multicast group at the same time, -// and both will receive data on it when bound to the multicast address. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastToTwoBoundToMulticastAddress) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - std::unique_ptr<FileDescriptor> receivers[2] = { - ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket())}; - - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - auto receiver_addr = V4Multicast(); - int bound_port = 0; - for (auto& receiver : receivers) { - ASSERT_THAT(setsockopt(receiver->get(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - EXPECT_EQ( - inet_addr(kMulticastAddress), - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_addr.s_addr); - // On the first iteration, save the port we are bound to. On the second - // iteration, verify the port is the same as the one from the first - // iteration. In other words, both sockets listen on the same port. - if (bound_port == 0) { - bound_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - } else { - EXPECT_EQ( - inet_addr(kMulticastAddress), - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_addr.s_addr); - EXPECT_EQ(bound_port, - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port); - } - - // Register to receive multicast packets. - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &group, sizeof(group)), - SyscallSucceeds()); - } - - // Send a multicast packet to the group and verify both receivers get it. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = bound_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - for (auto& receiver : receivers) { - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - } -} - -// Check that two sockets can join the same multicast group at the same time, -// and with one bound to the wildcard address and the other bound to the -// multicast address, both will receive data. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - TestSendMulticastToTwoBoundToAnyAndMulticastAddress) { - SKIP_IF(!found_net_interfaces_); - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - std::unique_ptr<FileDescriptor> receivers[2] = { - ASSERT_NO_ERRNO_AND_VALUE(NewSocket()), - ASSERT_NO_ERRNO_AND_VALUE(NewSocket())}; - - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - // The first receiver binds to the wildcard address. - auto receiver_addr = V4Any(); - int bound_port = 0; - for (auto& receiver : receivers) { - ASSERT_THAT(setsockopt(receiver->get(), SOL_SOCKET, SO_REUSEPORT, - &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - // On the first iteration, save the port we are bound to and change the - // receiver address from V4Any to V4Multicast so the second receiver binds - // to that. On the second iteration, verify the port is the same as the one - // from the first iteration but the address is different. - if (bound_port == 0) { - EXPECT_EQ( - htonl(INADDR_ANY), - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_addr.s_addr); - bound_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - receiver_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port = - bound_port; - } else { - EXPECT_EQ( - inet_addr(kMulticastAddress), - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_addr.s_addr); - EXPECT_EQ(bound_port, - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port); - } - - // Register to receive multicast packets. - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, - &group, sizeof(group)), - SyscallSucceeds()); - } - - // Send a multicast packet to the group and verify both receivers get it. - auto send_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&send_addr.addr)->sin_port = bound_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - for (auto& receiver : receivers) { - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT( - RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - } -} - -// Check that when receiving a looped-back multicast packet, its source address -// is not a multicast address. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - IpMulticastLoopbackFromAddr) { - SKIP_IF(!found_net_interfaces_); - - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - int receiver_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - - ip_mreq group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Connect to the multicast address. This binds us to the outgoing interface - // and allows us to get its IP (to be compared against the src-IP on the - // receiver side). - auto sendto_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = receiver_port; - ASSERT_THAT(RetryEINTR(connect)( - sender->get(), reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceeds()); - auto sender_addr = V4EmptyAddress(); - ASSERT_THAT( - getsockname(sender->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - &sender_addr.addr_len), - SyscallSucceeds()); - ASSERT_EQ(sizeof(struct sockaddr_in), sender_addr.addr_len); - sockaddr_in* sender_addr_in = - reinterpret_cast<sockaddr_in*>(&sender_addr.addr); - - // Send a multicast packet. - char send_buf[4] = {}; - ASSERT_THAT(RetryEINTR(send)(sender->get(), send_buf, sizeof(send_buf), 0), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Receive a multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - auto src_addr = V4EmptyAddress(); - ASSERT_THAT( - RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0, - reinterpret_cast<sockaddr*>(&src_addr.addr), - &src_addr.addr_len), - SyscallSucceedsWithValue(sizeof(recv_buf))); - ASSERT_EQ(sizeof(struct sockaddr_in), src_addr.addr_len); - sockaddr_in* src_addr_in = reinterpret_cast<sockaddr_in*>(&src_addr.addr); - - // Verify that the received source IP:port matches the sender one. - EXPECT_EQ(sender_addr_in->sin_port, src_addr_in->sin_port); - EXPECT_EQ(sender_addr_in->sin_addr.s_addr, src_addr_in->sin_addr.s_addr); -} - -// Check that when setting the IP_MULTICAST_IF option to both an index pointing -// to the loopback interface and an address pointing to the non-loopback -// interface, a multicast packet sent out uses the latter as its source address. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - IpMulticastLoopbackIfNicAndAddr) { - SKIP_IF(!found_net_interfaces_); - - // Create receiver, bind to ANY and join the multicast group. - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver_addr = V4Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - int receiver_port = - reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)->sin_port; - ip_mreqn group = {}; - group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress); - group.imr_ifindex = lo_if_idx_; - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, - sizeof(group)), - SyscallSucceeds()); - - // Set outgoing multicast interface config, with NIC and addr pointing to - // different interfaces. - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - ip_mreqn iface = {}; - iface.imr_ifindex = lo_if_idx_; - iface.imr_address = eth_if_addr_.sin_addr; - ASSERT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()); - - // Send a multicast packet. - auto sendto_addr = V4Multicast(); - reinterpret_cast<sockaddr_in*>(&sendto_addr.addr)->sin_port = receiver_port; - char send_buf[4] = {}; - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr.addr), - sendto_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Receive a multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - auto src_addr = V4EmptyAddress(); - ASSERT_THAT( - RetryEINTR(recvfrom)(receiver->get(), recv_buf, sizeof(recv_buf), 0, - reinterpret_cast<sockaddr*>(&src_addr.addr), - &src_addr.addr_len), - SyscallSucceedsWithValue(sizeof(recv_buf))); - ASSERT_EQ(sizeof(struct sockaddr_in), src_addr.addr_len); - sockaddr_in* src_addr_in = reinterpret_cast<sockaddr_in*>(&src_addr.addr); - - // FIXME (b/137781162): When sending a multicast packet use the proper logic - // to determine the packet's src-IP. - SKIP_IF(IsRunningOnGvisor()); - - // Verify the received source address. - EXPECT_EQ(eth_if_addr_.sin_addr.s_addr, src_addr_in->sin_addr.s_addr); -} - -// Check that when we are bound to one interface we can set IP_MULTICAST_IF to -// another interface. -TEST_P(IPv4UDPUnboundExternalNetworkingSocketTest, - IpMulticastLoopbackBindToOneIfSetMcastIfToAnother) { - SKIP_IF(!found_net_interfaces_); - - // FIXME (b/137790511): When bound to one interface it is not possible to set - // IP_MULTICAST_IF to a different interface. - SKIP_IF(IsRunningOnGvisor()); - - // Create sender and bind to eth interface. - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - ASSERT_THAT(bind(sender->get(), reinterpret_cast<sockaddr*>(ð_if_addr_), - sizeof(eth_if_addr_)), - SyscallSucceeds()); - - // Run through all possible combinations of index and address for - // IP_MULTICAST_IF that selects the loopback interface. - struct { - int imr_ifindex; - struct in_addr imr_address; - } test_data[] = { - {lo_if_idx_, {}}, - {0, lo_if_addr_.sin_addr}, - {lo_if_idx_, lo_if_addr_.sin_addr}, - {lo_if_idx_, eth_if_addr_.sin_addr}, - }; - for (auto t : test_data) { - ip_mreqn iface = {}; - iface.imr_ifindex = t.imr_ifindex; - iface.imr_address = t.imr_address; - EXPECT_THAT(setsockopt(sender->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, - sizeof(iface)), - SyscallSucceeds()) - << "imr_index=" << iface.imr_ifindex - << " imr_address=" << GetAddr4Str(&iface.imr_address); - } -} -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h deleted file mode 100644 index 20922ac1f..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019 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_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ - -#include "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to unbound IPv4 UDP sockets in a sandbox -// with external networking support. -using IPv4UDPUnboundExternalNetworkingSocketTest = - IPUDPUnboundExternalNetworkingSocketTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc deleted file mode 100644 index f6e64c157..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2019 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/syscalls/linux/socket_ipv4_udp_unbound_external_networking.h" - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketKind> GetSockets() { - return ApplyVec<SocketKind>( - IPv4UDPUnboundSocket, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P(IPv4UDPUnboundSockets, - IPv4UDPUnboundExternalNetworkingSocketTest, - ::testing::ValuesIn(GetSockets())); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc deleted file mode 100644 index f121c044d..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_ipv4_udp_unbound.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -INSTANTIATE_TEST_SUITE_P( - IPv4UDPSockets, IPv4UDPUnboundSocketTest, - ::testing::ValuesIn(ApplyVec<SocketKind>(IPv4UDPUnboundSocket, - AllBitwiseCombinations(List<int>{ - 0, SOCK_NONBLOCK})))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc deleted file mode 100644 index 8052bf404..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc +++ /dev/null @@ -1,32 +0,0 @@ -// 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 <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -INSTANTIATE_TEST_SUITE_P( - IPv4UDPSockets, IPv4UDPUnboundSocketNetlinkTest, - ::testing::ValuesIn(ApplyVec<SocketKind>(IPv4UDPUnboundSocket, - AllBitwiseCombinations(List<int>{ - 0, SOCK_NONBLOCK})))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc deleted file mode 100644 index bcbd2feac..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc +++ /dev/null @@ -1,94 +0,0 @@ -// 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 <sys/socket.h> -#include <sys/types.h> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to IPv4 UDP sockets. -using IPv4UDPUnboundSocketNogotsanTest = SimpleSocketTest; - -// Check that connect returns EAGAIN when out of local ephemeral ports. -// We disable S/R because this test creates a large number of sockets. -TEST_P(IPv4UDPUnboundSocketNogotsanTest, - UDPConnectPortExhaustion_NoRandomSave) { - auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - constexpr int kClients = 65536; - // Bind the first socket to the loopback and take note of the selected port. - auto addr = V4Loopback(); - ASSERT_THAT(bind(receiver1->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len), - SyscallSucceeds()); - socklen_t addr_len = addr.addr_len; - ASSERT_THAT(getsockname(receiver1->get(), - reinterpret_cast<sockaddr*>(&addr.addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, addr.addr_len); - - // Disable cooperative S/R as we are making too many syscalls. - DisableSave ds; - std::vector<std::unique_ptr<FileDescriptor>> sockets; - for (int i = 0; i < kClients; i++) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int ret = connect(s->get(), reinterpret_cast<sockaddr*>(&addr.addr), - addr.addr_len); - if (ret == 0) { - sockets.push_back(std::move(s)); - continue; - } - ASSERT_THAT(ret, SyscallFailsWithErrno(EAGAIN)); - break; - } -} - -// Check that bind returns EADDRINUSE when out of local ephemeral ports. -// We disable S/R because this test creates a large number of sockets. -TEST_P(IPv4UDPUnboundSocketNogotsanTest, UDPBindPortExhaustion_NoRandomSave) { - auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - constexpr int kClients = 65536; - auto addr = V4Loopback(); - // Disable cooperative S/R as we are making too many syscalls. - DisableSave ds; - std::vector<std::unique_ptr<FileDescriptor>> sockets; - for (int i = 0; i < kClients; i++) { - auto s = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - int ret = - bind(s->get(), reinterpret_cast<sockaddr*>(&addr.addr), addr.addr_len); - if (ret == 0) { - sockets.push_back(std::move(s)); - continue; - } - ASSERT_THAT(ret, SyscallFailsWithErrno(EADDRINUSE)); - break; - } -} - -INSTANTIATE_TEST_SUITE_P( - IPv4UDPSockets, IPv4UDPUnboundSocketNogotsanTest, - ::testing::ValuesIn(ApplyVec<SocketKind>(IPv4UDPUnboundSocket, - AllBitwiseCombinations(List<int>{ - 0, SOCK_NONBLOCK})))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc deleted file mode 100644 index 9a9ddc297..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc +++ /dev/null @@ -1,209 +0,0 @@ -// 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/syscalls/linux/socket_ipv4_udp_unbound_netlink.h" - -#include <arpa/inet.h> -#include <poll.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_netlink_route_util.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" - -namespace gvisor { -namespace testing { - -constexpr size_t kSendBufSize = 200; - -// Checks that the loopback interface considers itself bound to all IPs in an -// associated subnet. -TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - // Add an IP address to the loopback interface. - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - struct in_addr addr; - ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr)); - ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - }); - - auto snd_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto rcv_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - // Send from an unassigned address but an address that is in the subnet - // associated with the loopback interface. - TestAddress sender_addr("V4NotAssignd1"); - sender_addr.addr.ss_family = AF_INET; - sender_addr.addr_len = sizeof(sockaddr_in); - ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.2", - &(reinterpret_cast<sockaddr_in*>(&sender_addr.addr) - ->sin_addr.s_addr))); - ASSERT_THAT( - bind(snd_sock->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Send the packet to an unassigned address but an address that is in the - // subnet associated with the loopback interface. - TestAddress receiver_addr("V4NotAssigned2"); - receiver_addr.addr.ss_family = AF_INET; - receiver_addr.addr_len = sizeof(sockaddr_in); - ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.254", - &(reinterpret_cast<sockaddr_in*>(&receiver_addr.addr) - ->sin_addr.s_addr))); - ASSERT_THAT( - bind(rcv_sock->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(rcv_sock->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - ASSERT_EQ(receiver_addr_len, receiver_addr.addr_len); - char send_buf[kSendBufSize]; - RandomizeBuffer(send_buf, kSendBufSize); - ASSERT_THAT( - RetryEINTR(sendto)(snd_sock->get(), send_buf, kSendBufSize, 0, - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceedsWithValue(kSendBufSize)); - - // Check that we received the packet. - char recv_buf[kSendBufSize] = {}; - ASSERT_THAT(RetryEINTR(recv)(rcv_sock->get(), recv_buf, kSendBufSize, 0), - SyscallSucceedsWithValue(kSendBufSize)); - ASSERT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize)); -} - -// Tests that broadcast packets are delivered to all interested sockets -// (wildcard and broadcast address specified sockets). -// -// Note, we cannot test the IPv4 Broadcast (255.255.255.255) because we do -// not have a route to it. -TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) { - constexpr uint16_t kPort = 9876; - // Wait up to 20 seconds for the data. - constexpr int kPollTimeoutMs = 20000; - // Number of sockets per socket type. - constexpr int kNumSocketsPerType = 2; - - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - // Add an IP address to the loopback interface. - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - struct in_addr addr; - ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr)); - ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, - 24 /* prefixlen */, &addr, sizeof(addr))); - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - }); - - TestAddress broadcast_address("SubnetBroadcastAddress"); - broadcast_address.addr.ss_family = AF_INET; - broadcast_address.addr_len = sizeof(sockaddr_in); - auto broadcast_address_in = - reinterpret_cast<sockaddr_in*>(&broadcast_address.addr); - ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.255", - &broadcast_address_in->sin_addr.s_addr)); - broadcast_address_in->sin_port = htons(kPort); - - TestAddress any_address = V4Any(); - reinterpret_cast<sockaddr_in*>(&any_address.addr)->sin_port = htons(kPort); - - // We create sockets bound to both the wildcard address and the broadcast - // address to make sure both of these types of "broadcast interested" sockets - // receive broadcast packets. - std::vector<std::unique_ptr<FileDescriptor>> socks; - for (bool bind_wildcard : {false, true}) { - // Create multiple sockets for each type of "broadcast interested" - // socket so we can test that all sockets receive the broadcast packet. - for (int i = 0; i < kNumSocketsPerType; i++) { - auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto idx = socks.size(); - - ASSERT_THAT(setsockopt(sock->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)) - << "socks[" << idx << "]"; - - ASSERT_THAT(setsockopt(sock->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceedsWithValue(0)) - << "socks[" << idx << "]"; - - if (bind_wildcard) { - ASSERT_THAT( - bind(sock->get(), reinterpret_cast<sockaddr*>(&any_address.addr), - any_address.addr_len), - SyscallSucceeds()) - << "socks[" << idx << "]"; - } else { - ASSERT_THAT(bind(sock->get(), - reinterpret_cast<sockaddr*>(&broadcast_address.addr), - broadcast_address.addr_len), - SyscallSucceeds()) - << "socks[" << idx << "]"; - } - - socks.push_back(std::move(sock)); - } - } - - char send_buf[kSendBufSize]; - RandomizeBuffer(send_buf, kSendBufSize); - - // Broadcasts from each socket should be received by every socket (including - // the sending socket). - for (long unsigned int w = 0; w < socks.size(); w++) { - auto& w_sock = socks[w]; - ASSERT_THAT( - RetryEINTR(sendto)(w_sock->get(), send_buf, kSendBufSize, 0, - reinterpret_cast<sockaddr*>(&broadcast_address.addr), - broadcast_address.addr_len), - SyscallSucceedsWithValue(kSendBufSize)) - << "write socks[" << w << "]"; - - // Check that we received the packet on all sockets. - for (long unsigned int r = 0; r < socks.size(); r++) { - auto& r_sock = socks[r]; - - struct pollfd poll_fd = {r_sock->get(), POLLIN, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)) - << "write socks[" << w << "] & read socks[" << r << "]"; - - char recv_buf[kSendBufSize] = {}; - EXPECT_THAT(RetryEINTR(recv)(r_sock->get(), recv_buf, kSendBufSize, 0), - SyscallSucceedsWithValue(kSendBufSize)) - << "write socks[" << w << "] & read socks[" << r << "]"; - EXPECT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize)) - << "write socks[" << w << "] & read socks[" << r << "]"; - } - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h deleted file mode 100644 index 73e7836d5..000000000 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_NETLINK_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_NETLINK_UTIL_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to IPv4 UDP sockets. -using IPv4UDPUnboundSocketNetlinkTest = SimpleSocketTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_NETLINK_UTIL_H_ diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.cc b/test/syscalls/linux/socket_ipv6_udp_unbound.cc deleted file mode 100644 index 08526468e..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound.cc +++ /dev/null @@ -1,131 +0,0 @@ -// 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/syscalls/linux/socket_ipv6_udp_unbound.h" - -#include <arpa/inet.h> -#include <netinet/in.h> -#ifdef __linux__ -#include <linux/in6.h> -#endif // __linux__ -#include <net/if.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <cstdio> -#include <cstring> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Test that socket will receive IP_RECVORIGDSTADDR control message. -TEST_P(IPv6UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) { - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver_addr = V6Loopback(); - int level = SOL_IPV6; - int type = IPV6_RECVORIGDSTADDR; - - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - - // Retrieve the port bound by the receiver. - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - ASSERT_THAT( - connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - - // Get address and port bound by the sender. - sockaddr_storage sender_addr_storage; - socklen_t sender_addr_len = sizeof(sender_addr_storage); - ASSERT_THAT(getsockname(sender->get(), - reinterpret_cast<sockaddr*>(&sender_addr_storage), - &sender_addr_len), - SyscallSucceeds()); - ASSERT_EQ(sender_addr_len, sizeof(struct sockaddr_in6)); - - // Enable IP_RECVORIGDSTADDR on socket so that we get the original destination - // address of the datagram as auxiliary information in the control message. - ASSERT_THAT( - setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Prepare message to send. - constexpr size_t kDataLength = 1024; - msghdr sent_msg = {}; - iovec sent_iov = {}; - char sent_data[kDataLength]; - sent_iov.iov_base = sent_data; - sent_iov.iov_len = kDataLength; - sent_msg.msg_iov = &sent_iov; - sent_msg.msg_iovlen = 1; - sent_msg.msg_flags = 0; - - ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - msghdr received_msg = {}; - iovec received_iov = {}; - char received_data[kDataLength]; - char received_cmsg_buf[CMSG_SPACE(sizeof(sockaddr_in6))] = {}; - size_t cmsg_data_len = sizeof(sockaddr_in6); - received_iov.iov_base = received_data; - received_iov.iov_len = kDataLength; - received_msg.msg_iov = &received_iov; - received_msg.msg_iovlen = 1; - received_msg.msg_controllen = CMSG_LEN(cmsg_data_len); - received_msg.msg_control = received_cmsg_buf; - - ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/), - IsPosixErrorOkAndHolds(kDataLength)); - - cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len)); - EXPECT_EQ(cmsg->cmsg_level, level); - EXPECT_EQ(cmsg->cmsg_type, type); - - // Check that the received address in the control message matches the expected - // receiver's address. - sockaddr_in6 received_addr = {}; - memcpy(&received_addr, CMSG_DATA(cmsg), sizeof(received_addr)); - auto orig_receiver_addr = - reinterpret_cast<sockaddr_in6*>(&receiver_addr.addr); - EXPECT_EQ(memcmp(&received_addr.sin6_addr, &orig_receiver_addr->sin6_addr, - sizeof(in6_addr)), - 0); - EXPECT_EQ(received_addr.sin6_port, orig_receiver_addr->sin6_port); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.h b/test/syscalls/linux/socket_ipv6_udp_unbound.h deleted file mode 100644 index 71e160f73..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to IPv6 UDP sockets. -using IPv6UDPUnboundSocketTest = SimpleSocketTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_ diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc deleted file mode 100644 index 7364a1ea5..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc +++ /dev/null @@ -1,90 +0,0 @@ -// 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/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h" - -namespace gvisor { -namespace testing { - -TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) { - SKIP_IF(!found_net_interfaces_); - - auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - - auto receiver_addr = V6Any(); - ASSERT_THAT( - bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr), - receiver_addr.addr_len), - SyscallSucceeds()); - socklen_t receiver_addr_len = receiver_addr.addr_len; - ASSERT_THAT(getsockname(receiver->get(), - reinterpret_cast<sockaddr*>(&receiver_addr.addr), - &receiver_addr_len), - SyscallSucceeds()); - EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len); - - // Register to receive multicast packets. - auto multicast_addr = V6Multicast(); - ipv6_mreq group_req = { - .ipv6mr_multiaddr = - reinterpret_cast<sockaddr_in6*>(&multicast_addr.addr)->sin6_addr, - .ipv6mr_interface = - (unsigned int)ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo")), - }; - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, - &group_req, sizeof(group_req)), - SyscallSucceeds()); - - // Set the sender to the loopback interface. - auto sender_addr = V6Loopback(); - ASSERT_THAT( - bind(sender->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallSucceeds()); - - // Send a multicast packet. - auto send_addr = multicast_addr; - reinterpret_cast<sockaddr_in6*>(&send_addr.addr)->sin6_port = - reinterpret_cast<sockaddr_in6*>(&receiver_addr.addr)->sin6_port; - char send_buf[200]; - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - - // Check that we received the multicast packet. - char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), 0), - SyscallSucceedsWithValue(sizeof(recv_buf))); - - EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); - - // Leave the group and make sure we don't receive its multicast traffic. - ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, - &group_req, sizeof(group_req)), - SyscallSucceeds()); - RandomizeBuffer(send_buf, sizeof(send_buf)); - ASSERT_THAT(RetryEINTR(sendto)(sender->get(), send_buf, sizeof(send_buf), 0, - reinterpret_cast<sockaddr*>(&send_addr.addr), - send_addr.addr_len), - SyscallSucceedsWithValue(sizeof(send_buf))); - ASSERT_THAT(RetryEINTR(recv)(receiver->get(), recv_buf, sizeof(recv_buf), - MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h deleted file mode 100644 index 731ae0a1f..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h +++ /dev/null @@ -1,31 +0,0 @@ -// 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_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ - -#include "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to unbound IPv6 UDP sockets in a sandbox -// with external networking support. -using IPv6UDPUnboundExternalNetworkingSocketTest = - IPUDPUnboundExternalNetworkingSocketTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6yy_UDP_UNBOUND_EXTERNAL_NETWORKING_H_ diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc deleted file mode 100644 index 5c764b8fd..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc +++ /dev/null @@ -1,39 +0,0 @@ -// 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/syscalls/linux/socket_ipv6_udp_unbound_external_networking.h" - -#include <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketKind> GetSockets() { - return ApplyVec<SocketKind>( - IPv6UDPUnboundSocket, - AllBitwiseCombinations(List<int>{0, SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P(IPv6UDPUnboundSockets, - IPv6UDPUnboundExternalNetworkingSocketTest, - ::testing::ValuesIn(GetSockets())); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc deleted file mode 100644 index 058336ecc..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc +++ /dev/null @@ -1,32 +0,0 @@ -// 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 <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_ipv6_udp_unbound.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -INSTANTIATE_TEST_SUITE_P( - IPv6UDPSockets, IPv6UDPUnboundSocketTest, - ::testing::ValuesIn(ApplyVec<SocketKind>(IPv6UDPUnboundSocket, - AllBitwiseCombinations(List<int>{ - 0, SOCK_NONBLOCK})))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc deleted file mode 100644 index 17021ff82..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc +++ /dev/null @@ -1,32 +0,0 @@ -// 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 <vector> - -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -INSTANTIATE_TEST_SUITE_P( - IPv6UDPSockets, IPv6UDPUnboundSocketNetlinkTest, - ::testing::ValuesIn(ApplyVec<SocketKind>(IPv6UDPUnboundSocket, - AllBitwiseCombinations(List<int>{ - 0, SOCK_NONBLOCK})))); - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc deleted file mode 100644 index 2ee218231..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.cc +++ /dev/null @@ -1,53 +0,0 @@ -// 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/syscalls/linux/socket_ipv6_udp_unbound_netlink.h" - -#include <arpa/inet.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_netlink_route_util.h" -#include "test/util/capability_util.h" - -namespace gvisor { -namespace testing { - -// Checks that the loopback interface does not consider itself bound to all IPs -// in an associated subnet. -TEST_P(IPv6UDPUnboundSocketNetlinkTest, JoinSubnet) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - // Add an IP address to the loopback interface. - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - struct in6_addr addr; - EXPECT_EQ(1, inet_pton(AF_INET6, "2001:db8::1", &addr)); - EXPECT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET6, - /*prefixlen=*/64, &addr, sizeof(addr))); - - // Binding to an unassigned address but an address that is in the subnet - // associated with the loopback interface should fail. - TestAddress sender_addr("V6NotAssignd1"); - sender_addr.addr.ss_family = AF_INET6; - sender_addr.addr_len = sizeof(sockaddr_in6); - EXPECT_EQ(1, inet_pton(AF_INET6, "2001:db8::2", - reinterpret_cast<sockaddr_in6*>(&sender_addr.addr) - ->sin6_addr.s6_addr)); - auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); - EXPECT_THAT(bind(sock->get(), reinterpret_cast<sockaddr*>(&sender_addr.addr), - sender_addr.addr_len), - SyscallFailsWithErrno(EADDRNOTAVAIL)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h b/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h deleted file mode 100644 index 88098be82..000000000 --- a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h +++ /dev/null @@ -1,29 +0,0 @@ -// 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_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_NETLINK_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_NETLINK_UTIL_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to IPv6 UDP sockets. -using IPv6UDPUnboundSocketNetlinkTest = SimpleSocketTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_NETLINK_UTIL_H_ diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc deleted file mode 100644 index 5f8d7f981..000000000 --- a/test/syscalls/linux/socket_netdevice.cc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <linux/ethtool.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <linux/sockios.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include "gtest/gtest.h" -#include "absl/base/internal/endian.h" -#include "test/syscalls/linux/socket_netlink_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Tests for netdevice queries. - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::AnyOf; -using ::testing::Eq; - -TEST(NetdeviceTest, Loopback) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - // Prepare the request. - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - - // Check for a non-zero interface index. - ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - - // Check that the loopback is zero hardware address. - ASSERT_THAT(ioctl(sock.get(), SIOCGIFHWADDR, &ifr), SyscallSucceeds()); - EXPECT_EQ(ifr.ifr_hwaddr.sa_family, ARPHRD_LOOPBACK); - EXPECT_EQ(ifr.ifr_hwaddr.sa_data[0], 0); - EXPECT_EQ(ifr.ifr_hwaddr.sa_data[1], 0); - EXPECT_EQ(ifr.ifr_hwaddr.sa_data[2], 0); - EXPECT_EQ(ifr.ifr_hwaddr.sa_data[3], 0); - EXPECT_EQ(ifr.ifr_hwaddr.sa_data[4], 0); - EXPECT_EQ(ifr.ifr_hwaddr.sa_data[5], 0); -} - -TEST(NetdeviceTest, Netmask) { - // We need an interface index to identify the loopback device. - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - - // Use a netlink socket to get the netmask, which we'll then compare to the - // netmask obtained via ioctl. - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); - - struct request { - struct nlmsghdr hdr; - struct rtgenmsg rgm; - }; - - constexpr uint32_t kSeq = 12345; - - struct request req; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETADDR; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rgm.rtgen_family = AF_UNSPEC; - - // Iterate through messages until we find the one containing the prefix length - // (i.e. netmask) for the loopback device. - int prefixlen = -1; - ASSERT_NO_ERRNO(NetlinkRequestResponse( - fd, &req, sizeof(req), - [&](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) - << std::hex << hdr->nlmsg_flags; - - EXPECT_EQ(hdr->nlmsg_seq, kSeq); - EXPECT_EQ(hdr->nlmsg_pid, port); - - if (hdr->nlmsg_type != RTM_NEWADDR) { - return; - } - - // 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)); - if (ifaddrmsg->ifa_index == static_cast<uint32_t>(ifr.ifr_ifindex) && - ifaddrmsg->ifa_family == AF_INET) { - prefixlen = ifaddrmsg->ifa_prefixlen; - } - }, - false)); - - ASSERT_GE(prefixlen, 0); - - // Netmask is stored big endian in struct sockaddr_in, so we do the same for - // comparison. - uint32_t mask = 0xffffffff << (32 - prefixlen); - mask = absl::gbswap_32(mask); - - // Check that the loopback interface has the correct subnet mask. - 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); - EXPECT_EQ(sin->sin_addr.s_addr, mask); -} - -TEST(NetdeviceTest, InterfaceName) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - // Prepare the request. - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - - // Check for a non-zero interface index. - ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds()); - EXPECT_NE(ifr.ifr_ifindex, 0); - - // Check that SIOCGIFNAME finds the loopback interface. - snprintf(ifr.ifr_name, IFNAMSIZ, "foo"); - ASSERT_THAT(ioctl(sock.get(), SIOCGIFNAME, &ifr), SyscallSucceeds()); - EXPECT_STREQ(ifr.ifr_name, "lo"); -} - -TEST(NetdeviceTest, InterfaceFlags) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - // Prepare the request. - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - - // Check that SIOCGIFFLAGS marks the interface with IFF_LOOPBACK, IFF_UP, and - // IFF_RUNNING. - ASSERT_THAT(ioctl(sock.get(), SIOCGIFFLAGS, &ifr), SyscallSucceeds()); - EXPECT_EQ(ifr.ifr_flags & IFF_UP, IFF_UP); - EXPECT_EQ(ifr.ifr_flags & IFF_RUNNING, IFF_RUNNING); -} - -TEST(NetdeviceTest, InterfaceMTU) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - // Prepare the request. - struct ifreq ifr = {}; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - - // Check that SIOCGIFMTU returns a nonzero MTU. - ASSERT_THAT(ioctl(sock.get(), SIOCGIFMTU, &ifr), SyscallSucceeds()); - EXPECT_GT(ifr.ifr_mtu, 0); -} - -TEST(NetdeviceTest, EthtoolGetTSInfo) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0)); - - struct ethtool_ts_info tsi = {}; - tsi.cmd = ETHTOOL_GET_TS_INFO; // Get NIC's Timestamping capabilities. - - // Prepare the request. - struct ifreq ifr = {}; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - ifr.ifr_data = (void*)&tsi; - - // Check that SIOCGIFMTU returns a nonzero MTU. - if (IsRunningOnGvisor()) { - ASSERT_THAT(ioctl(sock.get(), SIOCETHTOOL, &ifr), - SyscallFailsWithErrno(EOPNOTSUPP)); - return; - } - ASSERT_THAT(ioctl(sock.get(), SIOCETHTOOL, &ifr), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink.cc b/test/syscalls/linux/socket_netlink.cc deleted file mode 100644 index 4ec0fd4fa..000000000 --- a/test/syscalls/linux/socket_netlink.cc +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <linux/netlink.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Tests for all netlink socket protocols. - -namespace gvisor { -namespace testing { - -namespace { - -// NetlinkTest parameter is the protocol to test. -using NetlinkTest = ::testing::TestWithParam<int>; - -// Netlink sockets must be SOCK_DGRAM or SOCK_RAW. -TEST_P(NetlinkTest, Types) { - const int protocol = GetParam(); - - EXPECT_THAT(socket(AF_NETLINK, SOCK_STREAM, protocol), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - EXPECT_THAT(socket(AF_NETLINK, SOCK_SEQPACKET, protocol), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - EXPECT_THAT(socket(AF_NETLINK, SOCK_RDM, protocol), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - EXPECT_THAT(socket(AF_NETLINK, SOCK_DCCP, protocol), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - EXPECT_THAT(socket(AF_NETLINK, SOCK_PACKET, protocol), - SyscallFailsWithErrno(ESOCKTNOSUPPORT)); - - int fd; - EXPECT_THAT(fd = socket(AF_NETLINK, SOCK_DGRAM, protocol), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - EXPECT_THAT(fd = socket(AF_NETLINK, SOCK_RAW, protocol), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_P(NetlinkTest, AutomaticPort) { - const int protocol = GetParam(); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, protocol)); - - struct sockaddr_nl addr = {}; - addr.nl_family = AF_NETLINK; - - EXPECT_THAT( - bind(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceeds()); - - socklen_t addrlen = sizeof(addr); - EXPECT_THAT(getsockname(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), - &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, sizeof(addr)); - // This is the only netlink socket in the process, so it should get the PID as - // the port id. - // - // N.B. Another process could theoretically have explicitly reserved our pid - // as a port ID, but that is very unlikely. - EXPECT_EQ(addr.nl_pid, getpid()); -} - -// Calling connect automatically binds to an automatic port. -TEST_P(NetlinkTest, ConnectBinds) { - const int protocol = GetParam(); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, protocol)); - - struct sockaddr_nl addr = {}; - addr.nl_family = AF_NETLINK; - - EXPECT_THAT(connect(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); - - socklen_t addrlen = sizeof(addr); - EXPECT_THAT(getsockname(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), - &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, sizeof(addr)); - - // Each test is running in a pid namespace, so another process can explicitly - // reserve our pid as a port ID. In this case, a negative portid value will be - // set. - if (static_cast<pid_t>(addr.nl_pid) > 0) { - EXPECT_EQ(addr.nl_pid, getpid()); - } - - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - - // Connecting again is allowed, but keeps the same port. - EXPECT_THAT(connect(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), - sizeof(addr)), - SyscallSucceeds()); - - addrlen = sizeof(addr); - EXPECT_THAT(getsockname(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), - &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, sizeof(addr)); - EXPECT_EQ(addr.nl_pid, getpid()); -} - -TEST_P(NetlinkTest, GetPeerName) { - const int protocol = GetParam(); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, protocol)); - - struct sockaddr_nl addr = {}; - socklen_t addrlen = sizeof(addr); - - EXPECT_THAT(getpeername(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), - &addrlen), - SyscallSucceeds()); - - EXPECT_EQ(addrlen, sizeof(addr)); - EXPECT_EQ(addr.nl_family, AF_NETLINK); - // Peer is the kernel if we didn't connect elsewhere. - EXPECT_EQ(addr.nl_pid, 0); -} - -INSTANTIATE_TEST_SUITE_P(ProtocolTest, NetlinkTest, - ::testing::Values(NETLINK_ROUTE, - NETLINK_KOBJECT_UEVENT)); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc deleted file mode 100644 index ee3c08770..000000000 --- a/test/syscalls/linux/socket_netlink_route.cc +++ /dev/null @@ -1,971 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <fcntl.h> -#include <ifaddrs.h> -#include <linux/if.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include <iostream> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" -#include "test/syscalls/linux/socket_netlink_route_util.h" -#include "test/syscalls/linux/socket_netlink_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Tests for NETLINK_ROUTE sockets. - -namespace gvisor { -namespace testing { - -namespace { - -constexpr uint32_t kSeq = 12345; - -using ::testing::AnyOf; -using ::testing::Eq; - -// Parameters for SockOptTest. They are: -// 0: Socket option to query. -// 1: A predicate to run on the returned sockopt value. Should return true if -// the value is considered ok. -// 2: A description of what the sockopt value is expected to be. Should complete -// the sentence "<value> was unexpected, expected <description>" -using SockOptTest = ::testing::TestWithParam< - std::tuple<int, std::function<bool(int)>, std::string>>; - -TEST_P(SockOptTest, GetSockOpt) { - int sockopt = std::get<0>(GetParam()); - auto verifier = std::get<1>(GetParam()); - std::string verifier_description = std::get<2>(GetParam()); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); - - int res; - socklen_t len = sizeof(res); - - EXPECT_THAT(getsockopt(fd.get(), SOL_SOCKET, sockopt, &res, &len), - SyscallSucceeds()); - - EXPECT_EQ(len, sizeof(res)); - EXPECT_TRUE(verifier(res)) << absl::StrFormat( - "getsockopt(%d, SOL_SOCKET, %d, &res, &len) => res=%d was unexpected, " - "expected %s", - fd.get(), sockopt, res, verifier_description); -} - -std::function<bool(int)> IsPositive() { - return [](int val) { return val > 0; }; -} - -std::function<bool(int)> IsEqual(int target) { - return [target](int val) { return val == target; }; -} - -INSTANTIATE_TEST_SUITE_P( - NetlinkRouteTest, SockOptTest, - ::testing::Values( - std::make_tuple(SO_SNDBUF, IsPositive(), "positive send buffer size"), - std::make_tuple(SO_RCVBUF, IsPositive(), - "positive receive buffer size"), - std::make_tuple(SO_TYPE, IsEqual(SOCK_RAW), - absl::StrFormat("SOCK_RAW (%d)", SOCK_RAW)), - std::make_tuple(SO_DOMAIN, IsEqual(AF_NETLINK), - absl::StrFormat("AF_NETLINK (%d)", AF_NETLINK)), - std::make_tuple(SO_PROTOCOL, IsEqual(NETLINK_ROUTE), - absl::StrFormat("NETLINK_ROUTE (%d)", NETLINK_ROUTE)), - std::make_tuple(SO_PASSCRED, IsEqual(0), "0"))); - -// Validates the reponses to RTM_GETLINK + NLM_F_DUMP. -void CheckGetLinkResponse(const struct nlmsghdr* hdr, int seq, int port) { - EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWLINK), Eq(NLMSG_DONE))); - - EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) - << std::hex << hdr->nlmsg_flags; - - EXPECT_EQ(hdr->nlmsg_seq, seq); - EXPECT_EQ(hdr->nlmsg_pid, port); - - if (hdr->nlmsg_type != RTM_NEWLINK) { - return; - } - - // RTM_NEWLINK contains at least the header and ifinfomsg. - EXPECT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); - - // TODO(mpratt): Check ifinfomsg contents and following attrs. -} - -TEST(NetlinkRouteTest, GetLinkDump) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); - - // Loopback is common among all tests, check that it's found. - bool loopbackFound = false; - ASSERT_NO_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) { - CheckGetLinkResponse(hdr, kSeq, port); - if (hdr->nlmsg_type != RTM_NEWLINK) { - return; - } - ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); - const struct ifinfomsg* msg = - reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); - std::cout << "Found interface idx=" << msg->ifi_index - << ", type=" << std::hex << msg->ifi_type << std::endl; - if (msg->ifi_type == ARPHRD_LOOPBACK) { - loopbackFound = true; - EXPECT_NE(msg->ifi_flags & IFF_LOOPBACK, 0); - } - })); - EXPECT_TRUE(loopbackFound); -} - -// CheckLinkMsg checks a netlink message against an expected link. -void CheckLinkMsg(const struct nlmsghdr* hdr, const Link& link) { - ASSERT_THAT(hdr->nlmsg_type, Eq(RTM_NEWLINK)); - ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg))); - const struct ifinfomsg* msg = - reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); - EXPECT_EQ(msg->ifi_index, link.index); - - const struct rtattr* rta = FindRtAttr(hdr, msg, IFLA_IFNAME); - EXPECT_NE(nullptr, rta) << "IFLA_IFNAME not found in message."; - if (rta != nullptr) { - std::string name(reinterpret_cast<const char*>(RTA_DATA(rta))); - EXPECT_EQ(name, link.name); - } -} - -TEST(NetlinkRouteTest, GetLinkByIndex) { - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - req.ifm.ifi_index = loopback_link.index; - - bool found = false; - ASSERT_NO_ERRNO(NetlinkRequestResponse( - fd, &req, sizeof(req), - [&](const struct nlmsghdr* hdr) { - CheckLinkMsg(hdr, loopback_link); - found = true; - }, - false)); - EXPECT_TRUE(found) << "Netlink response does not contain any links."; -} - -TEST(NetlinkRouteTest, GetLinkByName) { - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - struct rtattr rtattr; - char ifname[IFNAMSIZ]; - char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; - }; - - struct request req = {}; - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - req.rtattr.rta_type = IFLA_IFNAME; - req.rtattr.rta_len = RTA_LENGTH(loopback_link.name.size() + 1); - strncpy(req.ifname, loopback_link.name.c_str(), sizeof(req.ifname)); - req.hdr.nlmsg_len = - NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len); - - bool found = false; - ASSERT_NO_ERRNO(NetlinkRequestResponse( - fd, &req, sizeof(req), - [&](const struct nlmsghdr* hdr) { - CheckLinkMsg(hdr, loopback_link); - found = true; - }, - false)); - EXPECT_TRUE(found) << "Netlink response does not contain any links."; -} - -TEST(NetlinkRouteTest, GetLinkByIndexNotFound) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - req.ifm.ifi_index = 1234590; - - EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), - PosixErrorIs(ENODEV, ::testing::_)); -} - -TEST(NetlinkRouteTest, GetLinkByNameNotFound) { - const std::string name = "nodevice?!"; - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - struct rtattr rtattr; - char ifname[IFNAMSIZ]; - char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; - }; - - struct request req = {}; - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - req.rtattr.rta_type = IFLA_IFNAME; - req.rtattr.rta_len = RTA_LENGTH(name.size() + 1); - strncpy(req.ifname, name.c_str(), sizeof(req.ifname)); - req.hdr.nlmsg_len = - NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len); - - EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), - PosixErrorIs(ENODEV, ::testing::_)); -} - -TEST(NetlinkRouteTest, MsgHdrMsgUnsuppType) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - // If type & 0x3 is equal to 0x2, this means a get request - // which doesn't require CAP_SYS_ADMIN. - req.hdr.nlmsg_type = ((__RTM_MAX + 1024) & (~0x3)) | 0x2; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - - EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)), - PosixErrorIs(EOPNOTSUPP, ::testing::_)); -} - -TEST(NetlinkRouteTest, MsgHdrMsgTrunc) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - - struct iovec iov = {}; - iov.iov_base = &req; - iov.iov_len = sizeof(req); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - // No destination required; it defaults to pid 0, the kernel. - - ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - // Small enough to ensure that the response doesn't fit. - constexpr size_t kBufferSize = 10; - std::vector<char> buf(kBufferSize); - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), - SyscallSucceedsWithValue(kBufferSize)); - EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC); -} - -TEST(NetlinkRouteTest, SpliceFromPipe) { - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - FileDescriptor rfd(fds[0]); - FileDescriptor wfd(fds[1]); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - req.ifm.ifi_index = loopback_link.index; - - ASSERT_THAT(write(wfd.get(), &req, sizeof(req)), - SyscallSucceedsWithValue(sizeof(req))); - - EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0), - SyscallSucceedsWithValue(sizeof(req))); - close(wfd.release()); - EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0), - SyscallSucceedsWithValue(0)); - - bool found = false; - ASSERT_NO_ERRNO(NetlinkResponse( - fd, - [&](const struct nlmsghdr* hdr) { - CheckLinkMsg(hdr, loopback_link); - found = true; - }, - false)); - EXPECT_TRUE(found) << "Netlink response does not contain any links."; -} - -TEST(NetlinkRouteTest, MsgTruncMsgHdrMsgTrunc) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.ifm.ifi_family = AF_UNSPEC; - - struct iovec iov = {}; - iov.iov_base = &req; - iov.iov_len = sizeof(req); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - // No destination required; it defaults to pid 0, the kernel. - - ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - // Small enough to ensure that the response doesn't fit. - constexpr size_t kBufferSize = 10; - std::vector<char> buf(kBufferSize); - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - int res = 0; - ASSERT_THAT(res = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC), - SyscallSucceeds()); - EXPECT_GT(res, kBufferSize); - EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC); -} - -TEST(NetlinkRouteTest, ControlMessageIgnored) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); - - struct request { - struct nlmsghdr control_hdr; - struct nlmsghdr message_hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - - // This control message is ignored. We still receive a response for the - // following RTM_GETLINK. - req.control_hdr.nlmsg_len = sizeof(req.control_hdr); - req.control_hdr.nlmsg_type = NLMSG_DONE; - req.control_hdr.nlmsg_seq = kSeq; - - req.message_hdr.nlmsg_len = sizeof(req.message_hdr) + sizeof(req.ifm); - req.message_hdr.nlmsg_type = RTM_GETLINK; - req.message_hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.message_hdr.nlmsg_seq = kSeq; - - req.ifm.ifi_family = AF_UNSPEC; - - ASSERT_NO_ERRNO(NetlinkRequestResponse( - fd, &req, sizeof(req), - [&](const struct nlmsghdr* hdr) { - CheckGetLinkResponse(hdr, kSeq, port); - }, - false)); -} - -TEST(NetlinkRouteTest, GetAddrDump) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); - - struct request { - struct nlmsghdr hdr; - struct rtgenmsg rgm; - }; - - struct request req; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETADDR; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rgm.rtgen_family = AF_UNSPEC; - - ASSERT_NO_ERRNO(NetlinkRequestResponse( - fd, &req, sizeof(req), - [&](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) - << std::hex << hdr->nlmsg_flags; - - EXPECT_EQ(hdr->nlmsg_seq, kSeq); - EXPECT_EQ(hdr->nlmsg_pid, port); - - if (hdr->nlmsg_type != RTM_NEWADDR) { - return; - } - - // RTM_NEWADDR contains at least the header and ifaddrmsg. - EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg)); - - // TODO(mpratt): Check ifaddrmsg contents and following attrs. - }, - false)); -} - -TEST(NetlinkRouteTest, LookupAll) { - struct ifaddrs* if_addr_list = nullptr; - auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); }); - - // Not a syscall but we can use the syscall matcher as glibc sets errno. - ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds()); - - int count = 0; - for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) { - if (!i->ifa_addr || (i->ifa_addr->sa_family != AF_INET && - i->ifa_addr->sa_family != AF_INET6)) { - continue; - } - count++; - } - ASSERT_GT(count, 0); -} - -TEST(NetlinkRouteTest, AddAndRemoveAddr) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - // Don't do cooperative save/restore because netstack state is not restored. - // TODO(gvisor.dev/issue/4595): enable cooperative save tests. - const DisableSave ds; - - Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - - struct in_addr addr; - ASSERT_EQ(inet_pton(AF_INET, "10.0.0.1", &addr), 1); - - // Create should succeed, as no such address in kernel. - ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr))); - - Cleanup defer_addr_removal = Cleanup( - [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - // First delete should succeed, as address exists. - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - - // Second delete should fail, as address no longer exists. - EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr)), - PosixErrorIs(EADDRNOTAVAIL, ::testing::_)); - }); - - // Replace an existing address should succeed. - ASSERT_NO_ERRNO(LinkReplaceLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr))); - - // Create exclusive should fail, as we created the address above. - EXPECT_THAT(LinkAddExclusiveLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr)), - PosixErrorIs(EEXIST, ::testing::_)); -} - -// GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request. -TEST(NetlinkRouteTest, GetRouteDump) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get())); - - struct request { - struct nlmsghdr hdr; - struct rtmsg rtm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETROUTE; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rtm.rtm_family = AF_UNSPEC; - - bool routeFound = false; - bool dstFound = true; - ASSERT_NO_ERRNO(NetlinkRequestResponse( - fd, &req, sizeof(req), - [&](const struct nlmsghdr* hdr) { - // Validate the reponse to RTM_GETROUTE + NLM_F_DUMP. - EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWROUTE), Eq(NLMSG_DONE))); - - EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI) - << std::hex << hdr->nlmsg_flags; - - EXPECT_EQ(hdr->nlmsg_seq, kSeq); - EXPECT_EQ(hdr->nlmsg_pid, port); - - // The test should not proceed if it's not a RTM_NEWROUTE message. - if (hdr->nlmsg_type != RTM_NEWROUTE) { - return; - } - - // 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); - - int len = RTM_PAYLOAD(hdr); - bool rtDstFound = false; - 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; - } - } - - std::cout << std::endl; - - // If the test is running in a new network namespace, it will have only - // the local route table. - if (msg->rtm_table == RT_TABLE_MAIN || - (!IsRunningOnGvisor() && msg->rtm_table == RT_TABLE_LOCAL)) { - routeFound = true; - dstFound = rtDstFound && dstFound; - } - }, - false)); - // At least one route found in main route table. - EXPECT_TRUE(routeFound); - // Found RTA_DST for each route in main table. - 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 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. -TEST(NetlinkRouteTest, RecvmsgTrunc) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct rtgenmsg rgm; - }; - - struct request req; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETADDR; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rgm.rtgen_family = AF_UNSPEC; - - struct iovec iov = {}; - iov.iov_base = &req; - iov.iov_len = sizeof(req); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - iov.iov_base = NULL; - iov.iov_len = 0; - - int trunclen, trunclen2; - - // Note: This test assumes at least two messages are returned by the - // RTM_GETADDR request. That means at least one RTM_NEWLINK message and one - // NLMSG_DONE message. We cannot read all the messages without blocking - // because we would need to read the message into a buffer and check the - // nlmsg_type for NLMSG_DONE. However, the test depends on reading into a - // zero-length buffer. - - // First, call recvmsg with MSG_TRUNC. This will read the full message from - // the socket and return it's full length. Subsequent calls to recvmsg will - // read the next messages from the socket. - ASSERT_THAT(trunclen = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC), - SyscallSucceeds()); - - // Message should always be truncated. However, While the destination iov is - // zero length, MSG_TRUNC returns the size of the next message so it should - // not be zero. - ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); - ASSERT_NE(trunclen, 0); - // Returned length is at least the header and ifaddrmsg. - EXPECT_GE(trunclen, sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg)); - - // Reset the msg_flags to make sure that the recvmsg call is setting them - // properly. - msg.msg_flags = 0; - - // Make a second recvvmsg call to get the next message. - ASSERT_THAT(trunclen2 = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC), - SyscallSucceeds()); - ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); - ASSERT_NE(trunclen2, 0); - - // Assert that the received messages are not the same. - // - // We are calling recvmsg with a zero length buffer so we have no way to - // inspect the messages to make sure they are not equal in value. The best - // we can do is to compare their lengths. - ASSERT_NE(trunclen, trunclen2); -} - -// RecvmsgTruncPeek tests recvmsg with the combination of the MSG_TRUNC and -// MSG_PEEK flags and a zero length output buffer. This is normally used to -// read the full length of the next message on the socket without consuming -// it, so a properly sized buffer can be allocated to store the message. This -// test tests that scenario. -TEST(NetlinkRouteTest, RecvmsgTruncPeek) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct rtgenmsg rgm; - }; - - struct request req; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETADDR; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rgm.rtgen_family = AF_UNSPEC; - - struct iovec iov = {}; - iov.iov_base = &req; - iov.iov_len = sizeof(req); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - int type = -1; - do { - int peeklen; - int len; - - iov.iov_base = NULL; - iov.iov_len = 0; - - // Call recvmsg with MSG_PEEK and MSG_TRUNC. This will peek at the message - // and return it's full length. - // See: MSG_TRUNC http://man7.org/linux/man-pages/man2/recv.2.html - ASSERT_THAT( - peeklen = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_PEEK | MSG_TRUNC), - SyscallSucceeds()); - - // Message should always be truncated. - ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); - ASSERT_NE(peeklen, 0); - - // Reset the message flags for the next call. - msg.msg_flags = 0; - - // Make the actual call to recvmsg to get the actual data. We will use - // the length returned from the peek call for the allocated buffer size.. - std::vector<char> buf(peeklen); - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - ASSERT_THAT(len = RetryEINTR(recvmsg)(fd.get(), &msg, 0), - SyscallSucceeds()); - - // Message should not be truncated since we allocated the correct buffer - // size. - EXPECT_NE(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); - - // MSG_PEEK should have left data on the socket and the subsequent call - // with should have retrieved the same data. Both calls should have - // returned the message's full length so they should be equal. - ASSERT_NE(len, 0); - ASSERT_EQ(peeklen, len); - - for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data()); - NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) { - type = hdr->nlmsg_type; - } - } while (type != NLMSG_DONE && type != NLMSG_ERROR); -} - -// No SCM_CREDENTIALS are received without SO_PASSCRED set. -TEST(NetlinkRouteTest, NoPasscredNoCreds) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - ASSERT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOff, - sizeof(kSockOptOff)), - SyscallSucceeds()); - - struct request { - struct nlmsghdr hdr; - struct rtgenmsg rgm; - }; - - struct request req; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETADDR; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rgm.rtgen_family = AF_UNSPEC; - - struct iovec iov = {}; - iov.iov_base = &req; - iov.iov_len = sizeof(req); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - iov.iov_base = NULL; - iov.iov_len = 0; - - char control[CMSG_SPACE(sizeof(struct ucred))] = {}; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - // Note: This test assumes at least one message is returned by the - // RTM_GETADDR request. - ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - // No control messages. - EXPECT_EQ(CMSG_FIRSTHDR(&msg), nullptr); -} - -// SCM_CREDENTIALS are received with SO_PASSCRED set. -TEST(NetlinkRouteTest, PasscredCreds) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - ASSERT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - struct request { - struct nlmsghdr hdr; - struct rtgenmsg rgm; - }; - - struct request req; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETADDR; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = kSeq; - req.rgm.rtgen_family = AF_UNSPEC; - - struct iovec iov = {}; - iov.iov_base = &req; - iov.iov_len = sizeof(req); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - iov.iov_base = NULL; - iov.iov_len = 0; - - char control[CMSG_SPACE(sizeof(struct ucred))] = {}; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - // Note: This test assumes at least one message is returned by the - // RTM_GETADDR request. - ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), SyscallSucceeds()); - - struct ucred creds; - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(creds))); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); - - memcpy(&creds, CMSG_DATA(cmsg), sizeof(creds)); - - // The peer is the kernel, which is "PID" 0. - EXPECT_EQ(creds.pid, 0); - // The kernel identifies as root. Also allow nobody in case this test is - // running in a userns without root mapped. - EXPECT_THAT(creds.uid, AnyOf(Eq(0), Eq(65534))); - EXPECT_THAT(creds.gid, AnyOf(Eq(0), Eq(65534))); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_route_util.cc b/test/syscalls/linux/socket_netlink_route_util.cc deleted file mode 100644 index 46f749c7c..000000000 --- a/test/syscalls/linux/socket_netlink_route_util.cc +++ /dev/null @@ -1,223 +0,0 @@ -// 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/syscalls/linux/socket_netlink_route_util.h" - -#include <linux/if.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> - -#include "test/syscalls/linux/socket_netlink_util.h" - -namespace gvisor { -namespace testing { -namespace { - -constexpr uint32_t kSeq = 12345; - -// Types of address modifications that may be performed on an interface. -enum class LinkAddrModification { - kAdd, - kAddExclusive, - kReplace, - kDelete, -}; - -// Populates |hdr| with appripriate values for the modification type. -PosixError PopulateNlmsghdr(LinkAddrModification modification, - struct nlmsghdr* hdr) { - switch (modification) { - case LinkAddrModification::kAdd: - hdr->nlmsg_type = RTM_NEWADDR; - hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - return NoError(); - case LinkAddrModification::kAddExclusive: - hdr->nlmsg_type = RTM_NEWADDR; - hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; - return NoError(); - case LinkAddrModification::kReplace: - hdr->nlmsg_type = RTM_NEWADDR; - hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; - return NoError(); - case LinkAddrModification::kDelete: - hdr->nlmsg_type = RTM_DELADDR; - hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - return NoError(); - } - - return PosixError(EINVAL); -} - -// Adds or removes the specified address from the specified interface. -PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen, - LinkAddrModification modification) { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifaddrmsg ifaddr; - char attrbuf[512]; - }; - - struct request req = {}; - PosixError err = PopulateNlmsghdr(modification, &req.hdr); - if (!err.ok()) { - return err; - } - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifaddr)); - req.hdr.nlmsg_seq = kSeq; - req.ifaddr.ifa_index = index; - req.ifaddr.ifa_family = family; - req.ifaddr.ifa_prefixlen = prefixlen; - - struct rtattr* rta = reinterpret_cast<struct rtattr*>( - reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); - rta->rta_type = IFA_LOCAL; - rta->rta_len = RTA_LENGTH(addrlen); - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen); - memcpy(RTA_DATA(rta), addr, addrlen); - - return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); -} - -} // namespace - -PosixError DumpLinks( - const FileDescriptor& fd, uint32_t seq, - const std::function<void(const struct nlmsghdr* hdr)>& fn) { - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifm; - }; - - struct request req = {}; - req.hdr.nlmsg_len = sizeof(req); - req.hdr.nlmsg_type = RTM_GETLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.hdr.nlmsg_seq = seq; - req.ifm.ifi_family = AF_UNSPEC; - - return NetlinkRequestResponse(fd, &req, sizeof(req), fn, false); -} - -PosixErrorOr<std::vector<Link>> DumpLinks() { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); - - std::vector<Link> links; - RETURN_IF_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) { - if (hdr->nlmsg_type != RTM_NEWLINK || - hdr->nlmsg_len < NLMSG_SPACE(sizeof(struct ifinfomsg))) { - return; - } - const struct ifinfomsg* msg = - reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr)); - const auto* rta = FindRtAttr(hdr, msg, IFLA_IFNAME); - if (rta == nullptr) { - // Ignore links that do not have a name. - return; - } - - links.emplace_back(); - links.back().index = msg->ifi_index; - links.back().type = msg->ifi_type; - links.back().name = - std::string(reinterpret_cast<const char*>(RTA_DATA(rta))); - })); - return links; -} - -PosixErrorOr<Link> LoopbackLink() { - ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); - for (const auto& link : links) { - if (link.type == ARPHRD_LOOPBACK) { - return link; - } - } - return PosixError(ENOENT, "loopback link not found"); -} - -PosixError LinkAddLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kAdd); -} - -PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kAddExclusive); -} - -PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kReplace); -} - -PosixError LinkDelLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen) { - return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kDelete); -} - -PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change) { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifinfo; - char pad[NLMSG_ALIGNTO]; - }; - - struct request req = {}; - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)); - req.hdr.nlmsg_type = RTM_NEWLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - req.hdr.nlmsg_seq = kSeq; - req.ifinfo.ifi_index = index; - req.ifinfo.ifi_flags = flags; - req.ifinfo.ifi_change = change; - - return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); -} - -PosixError LinkSetMacAddr(int index, const void* addr, int addrlen) { - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifinfomsg ifinfo; - char attrbuf[512]; - }; - - struct request req = {}; - req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfo)); - req.hdr.nlmsg_type = RTM_NEWLINK; - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - req.hdr.nlmsg_seq = kSeq; - req.ifinfo.ifi_index = index; - - struct rtattr* rta = reinterpret_cast<struct rtattr*>( - reinterpret_cast<int8_t*>(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); - rta->rta_type = IFLA_ADDRESS; - rta->rta_len = RTA_LENGTH(addrlen); - req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(addrlen); - memcpy(RTA_DATA(rta), addr, addrlen); - - return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_route_util.h b/test/syscalls/linux/socket_netlink_route_util.h deleted file mode 100644 index eaa91ad79..000000000 --- a/test/syscalls/linux/socket_netlink_route_util.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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_SYSCALLS_LINUX_SOCKET_NETLINK_ROUTE_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NETLINK_ROUTE_UTIL_H_ - -#include <linux/netlink.h> -#include <linux/rtnetlink.h> - -#include <vector> - -#include "test/syscalls/linux/socket_netlink_util.h" - -namespace gvisor { -namespace testing { - -struct Link { - int index; - int16_t type; - std::string name; -}; - -PosixError DumpLinks(const FileDescriptor& fd, uint32_t seq, - const std::function<void(const struct nlmsghdr* hdr)>& fn); - -PosixErrorOr<std::vector<Link>> DumpLinks(); - -// Returns the loopback link on the system. ENOENT if not found. -PosixErrorOr<Link> LoopbackLink(); - -// LinkAddLocalAddr adds a new IFA_LOCAL address to the interface. -PosixError LinkAddLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); - -// LinkAddExclusiveLocalAddr adds a new IFA_LOCAL address with NLM_F_EXCL flag -// to the interface. -PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); - -// LinkReplaceLocalAddr replaces an IFA_LOCAL address on the interface. -PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); - -// LinkDelLocalAddr removes IFA_LOCAL attribute on the interface. -PosixError LinkDelLocalAddr(int index, int family, int prefixlen, - const void* addr, int addrlen); - -// LinkChangeFlags changes interface flags. E.g. IFF_UP. -PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change); - -// LinkSetMacAddr sets IFLA_ADDRESS attribute of the interface. -PosixError LinkSetMacAddr(int index, const void* addr, int addrlen); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NETLINK_ROUTE_UTIL_H_ diff --git a/test/syscalls/linux/socket_netlink_uevent.cc b/test/syscalls/linux/socket_netlink_uevent.cc deleted file mode 100644 index da425bed4..000000000 --- a/test/syscalls/linux/socket_netlink_uevent.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019 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 <linux/filter.h> -#include <linux/netlink.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_netlink_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -// Tests for NETLINK_KOBJECT_UEVENT sockets. -// -// gVisor never sends any messages on these sockets, so we don't test the events -// themselves. - -namespace gvisor { -namespace testing { - -namespace { - -// SO_PASSCRED can be enabled. Since no messages are sent in gVisor, we don't -// actually test receiving credentials. -TEST(NetlinkUeventTest, PassCred) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_KOBJECT_UEVENT)); - - EXPECT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); -} - -// SO_DETACH_FILTER fails without a filter already installed. -TEST(NetlinkUeventTest, DetachNoFilter) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_KOBJECT_UEVENT)); - - int opt; - EXPECT_THAT( - setsockopt(fd.get(), SOL_SOCKET, SO_DETACH_FILTER, &opt, sizeof(opt)), - SyscallFailsWithErrno(ENOENT)); -} - -// We can attach a BPF filter. -TEST(NetlinkUeventTest, AttachFilter) { - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_KOBJECT_UEVENT)); - - // Minimal BPF program: a single ret. - struct sock_filter filter = {0x6, 0, 0, 0}; - struct sock_fprog prog = {}; - prog.len = 1; - prog.filter = &filter; - - EXPECT_THAT( - setsockopt(fd.get(), SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)), - SyscallSucceeds()); - - int opt; - EXPECT_THAT( - setsockopt(fd.get(), SOL_SOCKET, SO_DETACH_FILTER, &opt, sizeof(opt)), - SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc deleted file mode 100644 index bdebea321..000000000 --- a/test/syscalls/linux/socket_netlink_util.cc +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_netlink_util.h" - -#include <linux/if_arp.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> -#include <sys/socket.h> - -#include <vector> - -#include "absl/strings/str_cat.h" -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<FileDescriptor> NetlinkBoundSocket(int protocol) { - FileDescriptor fd; - ASSIGN_OR_RETURN_ERRNO(fd, Socket(AF_NETLINK, SOCK_RAW, protocol)); - - struct sockaddr_nl addr = {}; - addr.nl_family = AF_NETLINK; - - RETURN_ERROR_IF_SYSCALL_FAIL( - bind(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))); - MaybeSave(); - - return std::move(fd); -} - -PosixErrorOr<uint32_t> NetlinkPortID(int fd) { - struct sockaddr_nl addr; - socklen_t addrlen = sizeof(addr); - - RETURN_ERROR_IF_SYSCALL_FAIL( - getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &addrlen)); - MaybeSave(); - - return static_cast<uint32_t>(addr.nl_pid); -} - -PosixError NetlinkRequestResponse( - const FileDescriptor& fd, void* request, size_t len, - const std::function<void(const struct nlmsghdr* hdr)>& fn, - bool expect_nlmsgerr) { - 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)); - - return NetlinkResponse(fd, fn, expect_nlmsgerr); -} - -PosixError NetlinkResponse( - const FileDescriptor& fd, - const std::function<void(const struct nlmsghdr* hdr)>& fn, - bool expect_nlmsgerr) { - constexpr size_t kBufferSize = 4096; - std::vector<char> buf(kBufferSize); - struct iovec iov = {}; - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - // If NLM_F_MULTI is set, response is a series of messages that ends with a - // NLMSG_DONE message. - int type = -1; - int flags = 0; - do { - int len; - RETURN_ERROR_IF_SYSCALL_FAIL(len = 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, len); hdr = NLMSG_NEXT(hdr, len)) { - fn(hdr); - flags = hdr->nlmsg_flags; - type = hdr->nlmsg_type; - // Done should include an integer payload for dump_done_errno. - // See net/netlink/af_netlink.c:netlink_dump - // Some tools like the 'ip' tool check the minimum length of the - // NLMSG_DONE message. - if (type == NLMSG_DONE) { - EXPECT_GE(hdr->nlmsg_len, NLMSG_LENGTH(sizeof(int))); - } - } - } while ((flags & NLM_F_MULTI) && type != NLMSG_DONE && type != NLMSG_ERROR); - - if (expect_nlmsgerr) { - EXPECT_EQ(type, NLMSG_ERROR); - } else if (flags & NLM_F_MULTI) { - EXPECT_EQ(type, NLMSG_DONE); - } - 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(); -} - -PosixError NetlinkRequestAckOrError(const FileDescriptor& fd, uint32_t seq, - void* request, size_t len) { - // Dummy negative number for no error message received. - // We won't get a negative error number so there will be no confusion. - int err = -42; - RETURN_IF_ERRNO(NetlinkRequestResponse( - fd, request, len, - [&](const struct nlmsghdr* hdr) { - EXPECT_EQ(NLMSG_ERROR, hdr->nlmsg_type); - EXPECT_EQ(hdr->nlmsg_seq, seq); - EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct nlmsgerr)); - - const struct nlmsgerr* msg = - reinterpret_cast<const struct nlmsgerr*>(NLMSG_DATA(hdr)); - err = -msg->error; - }, - true)); - return PosixError(err); -} - -const struct rtattr* FindRtAttr(const struct nlmsghdr* hdr, - const struct ifinfomsg* msg, int16_t attr) { - const int ifi_space = NLMSG_SPACE(sizeof(*msg)); - int attrlen = hdr->nlmsg_len - ifi_space; - const struct rtattr* rta = reinterpret_cast<const struct rtattr*>( - reinterpret_cast<const uint8_t*>(hdr) + NLMSG_ALIGN(ifi_space)); - for (; RTA_OK(rta, attrlen); rta = RTA_NEXT(rta, attrlen)) { - if (rta->rta_type == attr) { - return rta; - } - } - return nullptr; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h deleted file mode 100644 index f97276d44..000000000 --- a/test/syscalls/linux/socket_netlink_util.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_SOCKET_NETLINK_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_SOCKET_NETLINK_UTIL_H_ - -#include <sys/socket.h> -// socket.h has to be included before if_arp.h. -#include <linux/if_arp.h> -#include <linux/netlink.h> -#include <linux/rtnetlink.h> - -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" - -namespace gvisor { -namespace testing { - -// Returns a bound netlink socket. -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 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); - -// Call fn on all response netlink messages. -// -// To be used on requests with NLM_F_MULTI reponses. -PosixError NetlinkResponse( - const FileDescriptor& fd, - 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); - -// Send the passed request then expect and return an ack or error. -PosixError NetlinkRequestAckOrError(const FileDescriptor& fd, uint32_t seq, - void* request, size_t len); - -// Find rtnetlink attribute in message. -const struct rtattr* FindRtAttr(const struct nlmsghdr* hdr, - const struct ifinfomsg* msg, int16_t attr); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_SOCKET_NETLINK_UTIL_H_ diff --git a/test/syscalls/linux/socket_non_blocking.cc b/test/syscalls/linux/socket_non_blocking.cc deleted file mode 100644 index c3520cadd..000000000 --- a/test/syscalls/linux/socket_non_blocking.cc +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_non_blocking.h" - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -TEST_P(NonBlockingSocketPairTest, ReadNothingAvailable) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char buf[20] = {}; - ASSERT_THAT(ReadFd(sockets->first_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(NonBlockingSocketPairTest, RecvNothingAvailable) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char buf[20] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->first_fd(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(NonBlockingSocketPairTest, RecvMsgNothingAvailable) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct iovec iov; - char buf[20] = {}; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EAGAIN)); -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_non_blocking.h b/test/syscalls/linux/socket_non_blocking.h deleted file mode 100644 index bd3e02fd2..000000000 --- a/test/syscalls/linux/socket_non_blocking.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected non-blocking sockets. -using NonBlockingSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_non_stream.cc b/test/syscalls/linux/socket_non_stream.cc deleted file mode 100644 index c61817f14..000000000 --- a/test/syscalls/linux/socket_non_stream.cc +++ /dev/null @@ -1,337 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_non_stream.h" - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/ip_socket_test_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -TEST_P(NonStreamSocketPairTest, SendMsgTooLarge) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int sndbuf; - socklen_t length = sizeof(sndbuf); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &sndbuf, &length), - SyscallSucceeds()); - - // Make the call too large to fit in the send buffer. - const int buffer_size = 3 * sndbuf; - - EXPECT_THAT(SendLargeSendMsg(sockets, buffer_size, false /* reader */), - SyscallFailsWithErrno(EMSGSIZE)); -} - -// Stream sockets allow data sent with a single (e.g. write, sendmsg) syscall -// to be read in pieces with multiple (e.g. read, recvmsg) syscalls. -// -// SplitRecv checks that control messages can only be read on the first (e.g. -// read, recvmsg) syscall, even if it doesn't provide space for the control -// message. -TEST_P(NonStreamSocketPairTest, SplitRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data) / 2]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -// Stream sockets allow data sent with multiple sends to be read in a single -// recv. Datagram sockets do not. -// -// SingleRecv checks that only a single message is readable in a single recv. -TEST_P(NonStreamSocketPairTest, SingleRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data1, sizeof(sent_data1), 0), - SyscallSucceedsWithValue(sizeof(sent_data1))); - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data2, sizeof(sent_data2), 0), - SyscallSucceedsWithValue(sizeof(sent_data2))); - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); -} - -TEST_P(NonStreamSocketPairTest, RecvmsgMsghdrFlagMsgTrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data) / 2] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(received_data))); - - // Check that msghdr flags were updated. - EXPECT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); -} - -// Stream sockets allow data sent with multiple sends to be peeked at in a -// single recv. Datagram sockets (except for unix sockets) do not. -// -// SinglePeek checks that only a single message is peekable in a single recv. -TEST_P(NonStreamSocketPairTest, SinglePeek) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data1, sizeof(sent_data1), 0), - SyscallSucceedsWithValue(sizeof(sent_data1))); - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data2, sizeof(sent_data2), 0), - SyscallSucceedsWithValue(sizeof(sent_data2))); - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - for (int i = 0; i < 3; i++) { - memset(received_data, 0, sizeof(received_data)); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_PEEK), - SyscallSucceedsWithValue(sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - } - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(sent_data1), 0), - SyscallSucceedsWithValue(sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(sent_data2), 0), - SyscallSucceedsWithValue(sizeof(sent_data2))); - EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(sent_data2))); -} - -TEST_P(NonStreamSocketPairTest, MsgTruncTruncation) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data) / 2, MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); - - // Check that we didn't get any extra data. - EXPECT_NE(0, memcmp(sent_data + sizeof(sent_data) / 2, - received_data + sizeof(received_data) / 2, - sizeof(sent_data) / 2)); -} - -TEST_P(NonStreamSocketPairTest, MsgTruncTruncationRecvmsgMsghdrFlagMsgTrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data) / 2] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(received_data))); - - // Check that msghdr flags were updated. - EXPECT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); -} - -TEST_P(NonStreamSocketPairTest, MsgTruncSameSize) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(NonStreamSocketPairTest, MsgTruncNotFull) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[2 * sizeof(sent_data)]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -// This test tests reading from a socket with MSG_TRUNC and a zero length -// receive buffer. The user should be able to get the message length. -TEST_P(NonStreamSocketPairTest, RecvmsgMsgTruncZeroLen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - // The receive buffer is of zero length. - char received_data[0] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - // The syscall succeeds returning the full size of the message on the socket. - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data))); - - // Check that MSG_TRUNC is set on msghdr flags. - EXPECT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC); -} - -// This test tests reading from a socket with MSG_TRUNC | MSG_PEEK and a zero -// length receive buffer. The user should be able to get the message length -// without reading data off the socket. -TEST_P(NonStreamSocketPairTest, RecvmsgMsgTruncMsgPeekZeroLen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - // The receive buffer is of zero length. - char peek_data[0] = {}; - - struct iovec peek_iov; - peek_iov.iov_base = peek_data; - peek_iov.iov_len = sizeof(peek_data); - struct msghdr peek_msg = {}; - peek_msg.msg_flags = -1; - peek_msg.msg_iov = &peek_iov; - peek_msg.msg_iovlen = 1; - - // The syscall succeeds returning the full size of the message on the socket. - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &peek_msg, - MSG_TRUNC | MSG_PEEK), - SyscallSucceedsWithValue(sizeof(sent_data))); - - // Check that MSG_TRUNC is set on msghdr flags because the receive buffer is - // smaller than the message size. - EXPECT_EQ(peek_msg.msg_flags & MSG_TRUNC, MSG_TRUNC); - - char received_data[sizeof(sent_data)] = {}; - - struct iovec received_iov; - received_iov.iov_base = received_data; - received_iov.iov_len = sizeof(received_data); - struct msghdr received_msg = {}; - received_msg.msg_flags = -1; - received_msg.msg_iov = &received_iov; - received_msg.msg_iovlen = 1; - - // Next we can read the actual data. - ASSERT_THAT( - RetryEINTR(recvmsg)(sockets->second_fd(), &received_msg, MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - // Check that MSG_TRUNC is not set on msghdr flags because we read the whole - // message. - EXPECT_EQ(received_msg.msg_flags & MSG_TRUNC, 0); -} - -// This test tests reading from a socket with MSG_TRUNC | MSG_PEEK and a zero -// length receive buffer and MSG_DONTWAIT. The user should be able to get an -// EAGAIN or EWOULDBLOCK error response. -TEST_P(NonStreamSocketPairTest, RecvmsgTruncPeekDontwaitZeroLen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // NOTE: We don't send any data on the socket. - - // The receive buffer is of zero length. - char peek_data[0] = {}; - - struct iovec peek_iov; - peek_iov.iov_base = peek_data; - peek_iov.iov_len = sizeof(peek_data); - struct msghdr peek_msg = {}; - peek_msg.msg_flags = -1; - peek_msg.msg_iov = &peek_iov; - peek_msg.msg_iovlen = 1; - - // recvmsg fails with EAGAIN because no data is available on the socket. - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &peek_msg, - MSG_TRUNC | MSG_PEEK | MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_non_stream.h b/test/syscalls/linux/socket_non_stream.h deleted file mode 100644 index 469fbe6a2..000000000 --- a/test/syscalls/linux/socket_non_stream.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected non-stream sockets. -using NonStreamSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_ diff --git a/test/syscalls/linux/socket_non_stream_blocking.cc b/test/syscalls/linux/socket_non_stream_blocking.cc deleted file mode 100644 index b052f6e61..000000000 --- a/test/syscalls/linux/socket_non_stream_blocking.cc +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_non_stream_blocking.h" - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -TEST_P(BlockingNonStreamSocketPairTest, RecvLessThanBufferWaitAll) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[100]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data) * 2] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_WAITALL), - SyscallSucceedsWithValue(sizeof(sent_data))); -} - -// This test tests reading from a socket with MSG_TRUNC | MSG_PEEK and a zero -// length receive buffer and MSG_DONTWAIT. The recvmsg call should block on -// reading the data. -TEST_P(BlockingNonStreamSocketPairTest, - RecvmsgTruncPeekDontwaitZeroLenBlocking) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // NOTE: We don't initially send any data on the socket. - const int data_size = 10; - char sent_data[data_size]; - RandomizeBuffer(sent_data, data_size); - - // The receive buffer is of zero length. - char peek_data[0] = {}; - - struct iovec peek_iov; - peek_iov.iov_base = peek_data; - peek_iov.iov_len = sizeof(peek_data); - struct msghdr peek_msg = {}; - peek_msg.msg_flags = -1; - peek_msg.msg_iov = &peek_iov; - peek_msg.msg_iovlen = 1; - - ScopedThread t([&]() { - // The syscall succeeds returning the full size of the message on the - // socket. This should block until there is data on the socket. - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &peek_msg, - MSG_TRUNC | MSG_PEEK), - SyscallSucceedsWithValue(data_size)); - }); - - absl::SleepFor(absl::Seconds(1)); - ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), sent_data, data_size, 0), - SyscallSucceedsWithValue(data_size)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_non_stream_blocking.h b/test/syscalls/linux/socket_non_stream_blocking.h deleted file mode 100644 index 6e205a039..000000000 --- a/test/syscalls/linux/socket_non_stream_blocking.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of blocking connected non-stream -// sockets. -using BlockingNonStreamSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_stream.cc b/test/syscalls/linux/socket_stream.cc deleted file mode 100644 index 6522b2e01..000000000 --- a/test/syscalls/linux/socket_stream.cc +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_stream.h" - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -TEST_P(StreamSocketPairTest, SplitRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data) / 2]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data + sizeof(received_data), received_data, - sizeof(received_data))); -} - -// Stream sockets allow data sent with multiple sends to be read in a single -// recv. -// -// CoalescedRecv checks that multiple messages are readable in a single recv. -TEST_P(StreamSocketPairTest, CoalescedRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data1, sizeof(sent_data1), 0), - SyscallSucceedsWithValue(sizeof(sent_data1))); - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data2, sizeof(sent_data2), 0), - SyscallSucceedsWithValue(sizeof(sent_data2))); - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), - sizeof(sent_data2))); -} - -TEST_P(StreamSocketPairTest, WriteOneSideClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - const char str[] = "abc"; - ASSERT_THAT(write(sockets->second_fd(), str, 3), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(StreamSocketPairTest, RecvmsgMsghdrFlagsNoMsgTrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data) / 2] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(received_data))); - - // Check that msghdr flags were cleared (MSG_TRUNC was not set). - ASSERT_EQ(msg.msg_flags & MSG_TRUNC, 0); -} - -TEST_P(StreamSocketPairTest, RecvmsgTruncZeroLen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[0] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_TRUNC), - SyscallSucceedsWithValue(0)); - - // Check that msghdr flags were cleared (MSG_TRUNC was not set). - ASSERT_EQ(msg.msg_flags & MSG_TRUNC, 0); -} - -TEST_P(StreamSocketPairTest, RecvmsgTruncPeekZeroLen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[0] = {}; - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT( - RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_TRUNC | MSG_PEEK), - SyscallSucceedsWithValue(0)); - - // Check that msghdr flags were cleared (MSG_TRUNC was not set). - ASSERT_EQ(msg.msg_flags & MSG_TRUNC, 0); -} - -TEST_P(StreamSocketPairTest, MsgTrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)]; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data) / 2, MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_stream.h b/test/syscalls/linux/socket_stream.h deleted file mode 100644 index b837b8f8c..000000000 --- a/test/syscalls/linux/socket_stream.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of blocking and non-blocking -// connected stream sockets. -using StreamSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_ diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc deleted file mode 100644 index 538ee2268..000000000 --- a/test/syscalls/linux/socket_stream_blocking.cc +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_stream_blocking.h" - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -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()); -} - -// Random save may interrupt the call to sendmsg() in SendLargeSendMsg(), -// causing the write to be incomplete and the test to hang. -TEST_P(BlockingStreamSocketPairTest, SendMsgTooLarge_NoRandomSave) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int sndbuf; - socklen_t length = sizeof(sndbuf); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &sndbuf, &length), - SyscallSucceeds()); - - // Make the call too large to fit in the send buffer. - const int buffer_size = 3 * sndbuf; - - EXPECT_THAT(SendLargeSendMsg(sockets, buffer_size, true /* reader */), - SyscallSucceedsWithValue(buffer_size)); -} - -TEST_P(BlockingStreamSocketPairTest, RecvLessThanBuffer) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[100]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[200] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); -} - -// Test that MSG_WAITALL causes recv to block until all requested data is -// received. Random save can interrupt blocking and cause received data to be -// returned, even if the amount received is less than the full requested amount. -TEST_P(BlockingStreamSocketPairTest, RecvLessThanBufferWaitAll_NoRandomSave) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[100]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - constexpr auto kDuration = absl::Milliseconds(200); - auto before = Now(CLOCK_MONOTONIC); - - const ScopedThread t([&]() { - absl::SleepFor(kDuration); - - // Don't let saving after the write interrupt the blocking recv. - const DisableSave ds; - - ASSERT_THAT(write(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - }); - - char received_data[sizeof(sent_data) * 2] = {}; - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_WAITALL), - SyscallSucceedsWithValue(sizeof(received_data))); - - auto after = Now(CLOCK_MONOTONIC); - EXPECT_GE(after - before, kDuration); -} - -TEST_P(BlockingStreamSocketPairTest, SendTimeout) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - std::vector<char> buf(kPageSize); - // We don't know how much data the socketpair will buffer, so we may do an - // arbitrarily large number of writes; saving after each write causes this - // test's time to explode. - const DisableSave ds; - for (;;) { - int ret; - ASSERT_THAT( - ret = RetryEINTR(send)(sockets->first_fd(), buf.data(), buf.size(), 0), - ::testing::AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(EAGAIN))); - if (ret == -1) { - break; - } - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_stream_blocking.h b/test/syscalls/linux/socket_stream_blocking.h deleted file mode 100644 index 9fd19ff90..000000000 --- a/test/syscalls/linux/socket_stream_blocking.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of blocking connected stream -// sockets. -using BlockingStreamSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_ diff --git a/test/syscalls/linux/socket_stream_nonblock.cc b/test/syscalls/linux/socket_stream_nonblock.cc deleted file mode 100644 index 74d608741..000000000 --- a/test/syscalls/linux/socket_stream_nonblock.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_stream_nonblock.h" - -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -using ::testing::Le; - -TEST_P(NonBlockingStreamSocketPairTest, SendMsgTooLarge) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int sndbuf; - socklen_t length = sizeof(sndbuf); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &sndbuf, &length), - SyscallSucceeds()); - - // Make the call too large to fit in the send buffer. - const int buffer_size = 3 * sndbuf; - - EXPECT_THAT(SendLargeSendMsg(sockets, buffer_size, false /* reader */), - SyscallSucceedsWithValue(Le(buffer_size))); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_stream_nonblock.h b/test/syscalls/linux/socket_stream_nonblock.h deleted file mode 100644 index c3b7fad91..000000000 --- a/test/syscalls/linux/socket_stream_nonblock.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of non-blocking connected stream -// sockets. -using NonBlockingStreamSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_ diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc deleted file mode 100644 index b2a96086c..000000000 --- a/test/syscalls/linux/socket_test_util.cc +++ /dev/null @@ -1,957 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_test_util.h" - -#include <arpa/inet.h> -#include <poll.h> -#include <sys/socket.h> - -#include <memory> - -#include "gtest/gtest.h" -#include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/time/clock.h" -#include "absl/types/optional.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -Creator<SocketPair> SyscallSocketPairCreator(int domain, int type, - int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> { - int pair[2]; - RETURN_ERROR_IF_SYSCALL_FAIL(socketpair(domain, type, protocol, pair)); - MaybeSave(); // Save on successful creation. - return absl::make_unique<FDSocketPair>(pair[0], pair[1]); - }; -} - -Creator<FileDescriptor> SyscallSocketCreator(int domain, int type, - int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<FileDescriptor>> { - int fd = 0; - RETURN_ERROR_IF_SYSCALL_FAIL(fd = socket(domain, type, protocol)); - MaybeSave(); // Save on successful creation. - return absl::make_unique<FileDescriptor>(fd); - }; -} - -PosixErrorOr<struct sockaddr_un> UniqueUnixAddr(bool abstract, int domain) { - struct sockaddr_un addr = {}; - std::string path = NewTempAbsPathInDir("/tmp"); - if (path.size() >= sizeof(addr.sun_path)) { - return PosixError(EINVAL, - "Unable to generate a temp path of appropriate length"); - } - - if (abstract) { - // Indicate that the path is in the abstract namespace. - path[0] = 0; - } - memcpy(addr.sun_path, path.c_str(), path.length()); - addr.sun_family = domain; - return addr; -} - -Creator<SocketPair> AcceptBindSocketPairCreator(bool abstract, int domain, - int type, int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> { - ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un bind_addr, - UniqueUnixAddr(abstract, domain)); - ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un extra_addr, - UniqueUnixAddr(abstract, domain)); - - int bound; - RETURN_ERROR_IF_SYSCALL_FAIL(bound = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - RETURN_ERROR_IF_SYSCALL_FAIL( - bind(bound, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr))); - MaybeSave(); // Successful bind. - RETURN_ERROR_IF_SYSCALL_FAIL(listen(bound, /* backlog = */ 5)); - MaybeSave(); // Successful listen. - - int connected; - RETURN_ERROR_IF_SYSCALL_FAIL(connected = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - RETURN_ERROR_IF_SYSCALL_FAIL( - connect(connected, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr))); - MaybeSave(); // Successful connect. - - int accepted; - RETURN_ERROR_IF_SYSCALL_FAIL( - accepted = accept4(bound, nullptr, nullptr, - type & (SOCK_NONBLOCK | SOCK_CLOEXEC))); - MaybeSave(); // Successful connect. - - // Cleanup no longer needed resources. - RETURN_ERROR_IF_SYSCALL_FAIL(close(bound)); - MaybeSave(); // Dropped original socket. - - // Only unlink if path is not in abstract namespace. - if (bind_addr.sun_path[0] != 0) { - RETURN_ERROR_IF_SYSCALL_FAIL(unlink(bind_addr.sun_path)); - MaybeSave(); // Unlinked path. - } - - // accepted is before connected to destruct connected before accepted. - // Destructors for nonstatic member objects are called in the reverse order - // in which they appear in the class declaration. - return absl::make_unique<AddrFDSocketPair>(accepted, connected, bind_addr, - extra_addr); - }; -} - -Creator<SocketPair> FilesystemAcceptBindSocketPairCreator(int domain, int type, - int protocol) { - return AcceptBindSocketPairCreator(/* abstract= */ false, domain, type, - protocol); -} - -Creator<SocketPair> AbstractAcceptBindSocketPairCreator(int domain, int type, - int protocol) { - return AcceptBindSocketPairCreator(/* abstract= */ true, domain, type, - protocol); -} - -Creator<SocketPair> BidirectionalBindSocketPairCreator(bool abstract, - int domain, int type, - int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> { - ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr1, - UniqueUnixAddr(abstract, domain)); - ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr2, - UniqueUnixAddr(abstract, domain)); - - int sock1; - RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - RETURN_ERROR_IF_SYSCALL_FAIL( - bind(sock1, reinterpret_cast<struct sockaddr*>(&addr1), sizeof(addr1))); - MaybeSave(); // Successful bind. - - int sock2; - RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - RETURN_ERROR_IF_SYSCALL_FAIL( - bind(sock2, reinterpret_cast<struct sockaddr*>(&addr2), sizeof(addr2))); - MaybeSave(); // Successful bind. - - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock1, reinterpret_cast<struct sockaddr*>(&addr2), sizeof(addr2))); - MaybeSave(); // Successful connect. - - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock2, reinterpret_cast<struct sockaddr*>(&addr1), sizeof(addr1))); - MaybeSave(); // Successful connect. - - // Cleanup no longer needed resources. - - // Only unlink if path is not in abstract namespace. - if (addr1.sun_path[0] != 0) { - RETURN_ERROR_IF_SYSCALL_FAIL(unlink(addr1.sun_path)); - MaybeSave(); // Successful unlink. - } - - // Only unlink if path is not in abstract namespace. - if (addr2.sun_path[0] != 0) { - RETURN_ERROR_IF_SYSCALL_FAIL(unlink(addr2.sun_path)); - MaybeSave(); // Successful unlink. - } - - return absl::make_unique<FDSocketPair>(sock1, sock2); - }; -} - -Creator<SocketPair> FilesystemBidirectionalBindSocketPairCreator(int domain, - int type, - int protocol) { - return BidirectionalBindSocketPairCreator(/* abstract= */ false, domain, type, - protocol); -} - -Creator<SocketPair> AbstractBidirectionalBindSocketPairCreator(int domain, - int type, - int protocol) { - return BidirectionalBindSocketPairCreator(/* abstract= */ true, domain, type, - protocol); -} - -Creator<SocketPair> SocketpairGoferSocketPairCreator(int domain, int type, - int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> { - struct sockaddr_un addr = {}; - constexpr char kSocketGoferPath[] = "/socket"; - memcpy(addr.sun_path, kSocketGoferPath, sizeof(kSocketGoferPath)); - addr.sun_family = domain; - - int sock1; - RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock1, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))); - MaybeSave(); // Successful connect. - - int sock2; - RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock2, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))); - MaybeSave(); // Successful connect. - - // Make and close another socketpair to ensure that the duped ends of the - // first socketpair get closed. - // - // The problem is that there is no way to atomically send and close an FD. - // The closest that we can do is send and then immediately close the FD, - // which is what we do in the gofer. The gofer won't respond to another - // request until the reply is sent and the FD is closed, so forcing the - // gofer to handle another request will ensure that this has happened. - for (int i = 0; i < 2; i++) { - int sock; - RETURN_ERROR_IF_SYSCALL_FAIL(sock = socket(domain, type, protocol)); - RETURN_ERROR_IF_SYSCALL_FAIL(connect( - sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))); - RETURN_ERROR_IF_SYSCALL_FAIL(close(sock)); - } - - return absl::make_unique<FDSocketPair>(sock1, sock2); - }; -} - -Creator<SocketPair> SocketpairGoferFileSocketPairCreator(int flags) { - return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> { - constexpr char kSocketGoferPath[] = "/socket"; - - int sock1; - RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = - open(kSocketGoferPath, O_RDWR | flags)); - MaybeSave(); // Successful socket creation. - - int sock2; - RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = - open(kSocketGoferPath, O_RDWR | flags)); - MaybeSave(); // Successful socket creation. - - return absl::make_unique<FDSocketPair>(sock1, sock2); - }; -} - -Creator<SocketPair> UnboundSocketPairCreator(bool abstract, int domain, - int type, int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> { - ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr1, - UniqueUnixAddr(abstract, domain)); - ASSIGN_OR_RETURN_ERRNO(struct sockaddr_un addr2, - UniqueUnixAddr(abstract, domain)); - - int sock1; - RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - int sock2; - RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - return absl::make_unique<AddrFDSocketPair>(sock1, sock2, addr1, addr2); - }; -} - -Creator<SocketPair> FilesystemUnboundSocketPairCreator(int domain, int type, - int protocol) { - return UnboundSocketPairCreator(/* abstract= */ false, domain, type, - protocol); -} - -Creator<SocketPair> AbstractUnboundSocketPairCreator(int domain, int type, - int protocol) { - return UnboundSocketPairCreator(/* abstract= */ true, domain, type, protocol); -} - -void LocalhostAddr(struct sockaddr_in* addr, bool dual_stack) { - addr->sin_family = AF_INET; - addr->sin_port = htons(0); - inet_pton(AF_INET, "127.0.0.1", - reinterpret_cast<void*>(&addr->sin_addr.s_addr)); -} - -void LocalhostAddr(struct sockaddr_in6* addr, bool dual_stack) { - addr->sin6_family = AF_INET6; - addr->sin6_port = htons(0); - if (dual_stack) { - inet_pton(AF_INET6, "::ffff:127.0.0.1", - reinterpret_cast<void*>(&addr->sin6_addr.s6_addr)); - } else { - inet_pton(AF_INET6, "::1", - reinterpret_cast<void*>(&addr->sin6_addr.s6_addr)); - } - addr->sin6_scope_id = 0; -} - -template <typename T> -PosixErrorOr<T> BindIP(int fd, bool dual_stack) { - T addr = {}; - LocalhostAddr(&addr, dual_stack); - RETURN_ERROR_IF_SYSCALL_FAIL( - bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr))); - socklen_t addrlen = sizeof(addr); - RETURN_ERROR_IF_SYSCALL_FAIL( - getsockname(fd, reinterpret_cast<struct sockaddr*>(&addr), &addrlen)); - return addr; -} - -template <typename T> -PosixErrorOr<T> TCPBindAndListen(int fd, bool dual_stack) { - ASSIGN_OR_RETURN_ERRNO(T addr, BindIP<T>(fd, dual_stack)); - RETURN_ERROR_IF_SYSCALL_FAIL(listen(fd, /* backlog = */ 5)); - return addr; -} - -template <typename T> -PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> -CreateTCPConnectAcceptSocketPair(int bound, int connected, int type, - bool dual_stack, T bind_addr) { - int connect_result = 0; - RETURN_ERROR_IF_SYSCALL_FAIL( - (connect_result = RetryEINTR(connect)( - connected, reinterpret_cast<struct sockaddr*>(&bind_addr), - sizeof(bind_addr))) == -1 && - errno == EINPROGRESS - ? 0 - : connect_result); - MaybeSave(); // Successful connect. - - if (connect_result == -1) { - struct pollfd connect_poll = {connected, POLLOUT | POLLERR | POLLHUP, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(poll)(&connect_poll, 1, 0)); - int error = 0; - socklen_t errorlen = sizeof(error); - RETURN_ERROR_IF_SYSCALL_FAIL( - getsockopt(connected, SOL_SOCKET, SO_ERROR, &error, &errorlen)); - errno = error; - RETURN_ERROR_IF_SYSCALL_FAIL( - /* connect */ error == 0 ? 0 : -1); - } - - int accepted = -1; - struct pollfd accept_poll = {bound, POLLIN, 0}; - while (accepted == -1) { - RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(poll)(&accept_poll, 1, 0)); - - RETURN_ERROR_IF_SYSCALL_FAIL( - (accepted = RetryEINTR(accept4)( - bound, nullptr, nullptr, type & (SOCK_NONBLOCK | SOCK_CLOEXEC))) == - -1 && - errno == EAGAIN - ? 0 - : accepted); - } - MaybeSave(); // Successful accept. - - T extra_addr = {}; - LocalhostAddr(&extra_addr, dual_stack); - return absl::make_unique<AddrFDSocketPair>(connected, accepted, bind_addr, - extra_addr); -} - -template <typename T> -PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> CreateTCPAcceptBindSocketPair( - int bound, int connected, int type, bool dual_stack) { - ASSIGN_OR_RETURN_ERRNO(T bind_addr, TCPBindAndListen<T>(bound, dual_stack)); - - auto result = CreateTCPConnectAcceptSocketPair(bound, connected, type, - dual_stack, bind_addr); - - // Cleanup no longer needed resources. - RETURN_ERROR_IF_SYSCALL_FAIL(close(bound)); - MaybeSave(); // Successful close. - - return result; -} - -Creator<SocketPair> TCPAcceptBindSocketPairCreator(int domain, int type, - int protocol, - bool dual_stack) { - return [=]() -> PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> { - int bound; - RETURN_ERROR_IF_SYSCALL_FAIL(bound = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - int connected; - RETURN_ERROR_IF_SYSCALL_FAIL(connected = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - if (domain == AF_INET) { - return CreateTCPAcceptBindSocketPair<sockaddr_in>(bound, connected, type, - dual_stack); - } - return CreateTCPAcceptBindSocketPair<sockaddr_in6>(bound, connected, type, - dual_stack); - }; -} - -Creator<SocketPair> TCPAcceptBindPersistentListenerSocketPairCreator( - int domain, int type, int protocol, bool dual_stack) { - // These are lazily initialized below, on the first call to the returned - // lambda. These values are private to each returned lambda, but shared across - // invocations of a specific lambda. - // - // The sharing allows pairs created with the same parameters to share a - // listener. This prevents future connects from failing if the connecting - // socket selects a port which had previously been used by a listening socket - // that still has some connections in TIME-WAIT. - // - // The lazy initialization is to avoid creating sockets during parameter - // enumeration. This is important because parameters are enumerated during the - // build process where networking may not be available. - auto listener = std::make_shared<absl::optional<int>>(absl::optional<int>()); - auto addr4 = std::make_shared<absl::optional<sockaddr_in>>( - absl::optional<sockaddr_in>()); - auto addr6 = std::make_shared<absl::optional<sockaddr_in6>>( - absl::optional<sockaddr_in6>()); - - return [=]() -> PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> { - int connected; - RETURN_ERROR_IF_SYSCALL_FAIL(connected = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - // Share the listener across invocations. - if (!listener->has_value()) { - int fd = socket(domain, type, protocol); - if (fd < 0) { - return PosixError(errno, absl::StrCat("socket(", domain, ", ", type, - ", ", protocol, ")")); - } - listener->emplace(fd); - MaybeSave(); // Successful socket creation. - } - - // Bind the listener once, but create a new connect/accept pair each - // time. - if (domain == AF_INET) { - if (!addr4->has_value()) { - addr4->emplace( - TCPBindAndListen<sockaddr_in>(listener->value(), dual_stack) - .ValueOrDie()); - } - return CreateTCPConnectAcceptSocketPair(listener->value(), connected, - type, dual_stack, addr4->value()); - } - if (!addr6->has_value()) { - addr6->emplace( - TCPBindAndListen<sockaddr_in6>(listener->value(), dual_stack) - .ValueOrDie()); - } - return CreateTCPConnectAcceptSocketPair(listener->value(), connected, type, - dual_stack, addr6->value()); - }; -} - -template <typename T> -PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> CreateUDPBoundSocketPair( - int sock1, int sock2, int type, bool dual_stack) { - ASSIGN_OR_RETURN_ERRNO(T addr1, BindIP<T>(sock1, dual_stack)); - ASSIGN_OR_RETURN_ERRNO(T addr2, BindIP<T>(sock2, dual_stack)); - - return absl::make_unique<AddrFDSocketPair>(sock1, sock2, addr1, addr2); -} - -template <typename T> -PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> -CreateUDPBidirectionalBindSocketPair(int sock1, int sock2, int type, - bool dual_stack) { - ASSIGN_OR_RETURN_ERRNO( - auto socks, CreateUDPBoundSocketPair<T>(sock1, sock2, type, dual_stack)); - - // Connect sock1 to sock2. - RETURN_ERROR_IF_SYSCALL_FAIL(connect(socks->first_fd(), socks->second_addr(), - socks->second_addr_size())); - MaybeSave(); // Successful connection. - - // Connect sock2 to sock1. - RETURN_ERROR_IF_SYSCALL_FAIL(connect(socks->second_fd(), socks->first_addr(), - socks->first_addr_size())); - MaybeSave(); // Successful connection. - - return socks; -} - -Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type, - int protocol, - bool dual_stack) { - return [=]() -> PosixErrorOr<std::unique_ptr<AddrFDSocketPair>> { - int sock1; - RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - int sock2; - RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - if (domain == AF_INET) { - return CreateUDPBidirectionalBindSocketPair<sockaddr_in>( - sock1, sock2, type, dual_stack); - } - return CreateUDPBidirectionalBindSocketPair<sockaddr_in6>(sock1, sock2, - type, dual_stack); - }; -} - -Creator<SocketPair> UDPUnboundSocketPairCreator(int domain, int type, - int protocol, bool dual_stack) { - return [=]() -> PosixErrorOr<std::unique_ptr<FDSocketPair>> { - int sock1; - RETURN_ERROR_IF_SYSCALL_FAIL(sock1 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - int sock2; - RETURN_ERROR_IF_SYSCALL_FAIL(sock2 = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - return absl::make_unique<FDSocketPair>(sock1, sock2); - }; -} - -SocketPairKind Reversed(SocketPairKind const& base) { - auto const& creator = base.creator; - return SocketPairKind{ - absl::StrCat("reversed ", base.description), base.domain, base.type, - base.protocol, - [creator]() -> PosixErrorOr<std::unique_ptr<ReversedSocketPair>> { - ASSIGN_OR_RETURN_ERRNO(auto creator_value, creator()); - return absl::make_unique<ReversedSocketPair>(std::move(creator_value)); - }}; -} - -Creator<FileDescriptor> UnboundSocketCreator(int domain, int type, - int protocol) { - return [=]() -> PosixErrorOr<std::unique_ptr<FileDescriptor>> { - int sock; - RETURN_ERROR_IF_SYSCALL_FAIL(sock = socket(domain, type, protocol)); - MaybeSave(); // Successful socket creation. - - return absl::make_unique<FileDescriptor>(sock); - }; -} - -std::vector<SocketPairKind> IncludeReversals(std::vector<SocketPairKind> vec) { - return ApplyVecToVec<SocketPairKind>(std::vector<Middleware>{NoOp, Reversed}, - vec); -} - -SocketPairKind NoOp(SocketPairKind const& base) { return base; } - -void TransferTest(int fd1, int fd2) { - char buf1[20]; - RandomizeBuffer(buf1, sizeof(buf1)); - ASSERT_THAT(WriteFd(fd1, buf1, sizeof(buf1)), - SyscallSucceedsWithValue(sizeof(buf1))); - - char buf2[20]; - ASSERT_THAT(ReadFd(fd2, buf2, sizeof(buf2)), - SyscallSucceedsWithValue(sizeof(buf2))); - - EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); - - RandomizeBuffer(buf1, sizeof(buf1)); - ASSERT_THAT(WriteFd(fd2, buf1, sizeof(buf1)), - SyscallSucceedsWithValue(sizeof(buf1))); - - ASSERT_THAT(ReadFd(fd1, buf2, sizeof(buf2)), - SyscallSucceedsWithValue(sizeof(buf2))); - - EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); -} - -// Initializes the given buffer with random data. -void RandomizeBuffer(char* ptr, size_t len) { - uint32_t seed = time(nullptr); - for (size_t i = 0; i < len; ++i) { - ptr[i] = static_cast<char>(rand_r(&seed)); - } -} - -size_t CalculateUnixSockAddrLen(const char* sun_path) { - // Abstract addresses always return the full length. - if (sun_path[0] == 0) { - return sizeof(sockaddr_un); - } - // Filesystem addresses use the address length plus the 2 byte sun_family - // and null terminator. - return strlen(sun_path) + 3; -} - -struct sockaddr_storage AddrFDSocketPair::to_storage(const sockaddr_un& addr) { - struct sockaddr_storage addr_storage = {}; - memcpy(&addr_storage, &addr, sizeof(addr)); - return addr_storage; -} - -struct sockaddr_storage AddrFDSocketPair::to_storage(const sockaddr_in& addr) { - struct sockaddr_storage addr_storage = {}; - memcpy(&addr_storage, &addr, sizeof(addr)); - return addr_storage; -} - -struct sockaddr_storage AddrFDSocketPair::to_storage(const sockaddr_in6& addr) { - struct sockaddr_storage addr_storage = {}; - memcpy(&addr_storage, &addr, sizeof(addr)); - return addr_storage; -} - -SocketKind SimpleSocket(int fam, int type, int proto) { - return SocketKind{ - absl::StrCat("Family ", fam, ", type ", type, ", proto ", proto), fam, - type, proto, SyscallSocketCreator(fam, type, proto)}; -} - -ssize_t SendLargeSendMsg(const std::unique_ptr<SocketPair>& sockets, - size_t size, bool reader) { - const int rfd = sockets->second_fd(); - ScopedThread t([rfd, size, reader] { - if (!reader) { - return; - } - - // Potentially too many syscalls in the loop. - const DisableSave ds; - - std::vector<char> buf(size); - size_t total = 0; - - while (total < size) { - int ret = read(rfd, buf.data(), buf.size()); - if (ret == -1 && errno == EAGAIN) { - continue; - } - if (ret > 0) { - total += ret; - } - - // Assert to return on first failure. - ASSERT_THAT(ret, SyscallSucceeds()); - } - }); - - std::vector<char> buf(size); - - struct iovec iov = {}; - iov.iov_base = buf.data(); - iov.iov_len = buf.size(); - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - return RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0); -} - -namespace internal { -PosixErrorOr<int> TryPortAvailable(int port, AddressFamily family, - SocketType type, bool reuse_addr) { - if (port < 0) { - return PosixError(EINVAL, "Invalid port"); - } - - // Both Ipv6 and Dualstack are AF_INET6. - int sock_fam = (family == AddressFamily::kIpv4 ? AF_INET : AF_INET6); - int sock_type = (type == SocketType::kTcp ? SOCK_STREAM : SOCK_DGRAM); - ASSIGN_OR_RETURN_ERRNO(auto fd, Socket(sock_fam, sock_type, 0)); - - if (reuse_addr) { - int one = 1; - RETURN_ERROR_IF_SYSCALL_FAIL( - setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))); - } - - // Try to bind. - sockaddr_storage storage = {}; - int storage_size = 0; - if (family == AddressFamily::kIpv4) { - sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&storage); - storage_size = sizeof(*addr); - addr->sin_family = AF_INET; - addr->sin_port = htons(port); - addr->sin_addr.s_addr = htonl(INADDR_ANY); - } else { - sockaddr_in6* addr = reinterpret_cast<sockaddr_in6*>(&storage); - storage_size = sizeof(*addr); - addr->sin6_family = AF_INET6; - addr->sin6_port = htons(port); - if (family == AddressFamily::kDualStack) { - inet_pton(AF_INET6, "::ffff:0.0.0.0", - reinterpret_cast<void*>(&addr->sin6_addr.s6_addr)); - } else { - addr->sin6_addr = in6addr_any; - } - } - - RETURN_ERROR_IF_SYSCALL_FAIL( - bind(fd.get(), reinterpret_cast<sockaddr*>(&storage), storage_size)); - - // If the user specified 0 as the port, we will return the port that the - // kernel gave us, otherwise we will validate that this socket bound to the - // requested port. - sockaddr_storage bound_storage = {}; - socklen_t bound_storage_size = sizeof(bound_storage); - RETURN_ERROR_IF_SYSCALL_FAIL( - getsockname(fd.get(), reinterpret_cast<sockaddr*>(&bound_storage), - &bound_storage_size)); - - int available_port = -1; - if (bound_storage.ss_family == AF_INET) { - sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&bound_storage); - available_port = ntohs(addr->sin_port); - } else if (bound_storage.ss_family == AF_INET6) { - sockaddr_in6* addr = reinterpret_cast<sockaddr_in6*>(&bound_storage); - available_port = ntohs(addr->sin6_port); - } else { - return PosixError(EPROTOTYPE, "Getsockname returned invalid family"); - } - - // If we requested a specific port make sure our bound port is that port. - if (port != 0 && available_port != port) { - return PosixError(EINVAL, - absl::StrCat("Bound port ", available_port, - " was not equal to requested port ", port)); - } - - // If we're trying to do a TCP socket, let's also try to listen. - if (type == SocketType::kTcp) { - RETURN_ERROR_IF_SYSCALL_FAIL(listen(fd.get(), 1)); - } - - return available_port; -} -} // namespace internal - -PosixErrorOr<int> SendMsg(int sock, msghdr* msg, char buf[], int buf_size) { - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = buf_size; - msg->msg_iov = &iov; - msg->msg_iovlen = 1; - - int ret; - RETURN_ERROR_IF_SYSCALL_FAIL(ret = RetryEINTR(sendmsg)(sock, msg, 0)); - return ret; -} - -PosixErrorOr<int> RecvTimeout(int sock, char buf[], int buf_size, int timeout) { - fd_set rfd; - struct timeval to = {.tv_sec = timeout, .tv_usec = 0}; - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - int ret; - RETURN_ERROR_IF_SYSCALL_FAIL(ret = select(1, &rfd, NULL, NULL, &to)); - RETURN_ERROR_IF_SYSCALL_FAIL( - ret = RetryEINTR(recv)(sock, buf, buf_size, MSG_DONTWAIT)); - return ret; -} - -PosixErrorOr<int> RecvMsgTimeout(int sock, struct msghdr* msg, int timeout) { - fd_set rfd; - struct timeval to = {.tv_sec = timeout, .tv_usec = 0}; - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - - int ret; - RETURN_ERROR_IF_SYSCALL_FAIL(ret = select(1, &rfd, NULL, NULL, &to)); - RETURN_ERROR_IF_SYSCALL_FAIL( - ret = RetryEINTR(recvmsg)(sock, msg, MSG_DONTWAIT)); - return ret; -} - -void RecvNoData(int sock) { - char data = 0; - struct iovec iov; - iov.iov_base = &data; - iov.iov_len = 1; - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -TestAddress TestAddress::WithPort(uint16_t port) const { - TestAddress addr = *this; - switch (addr.family()) { - case AF_INET: - reinterpret_cast<sockaddr_in*>(&addr.addr)->sin_port = htons(port); - break; - case AF_INET6: - reinterpret_cast<sockaddr_in6*>(&addr.addr)->sin6_port = htons(port); - break; - } - return addr; -} - -TestAddress V4Any() { - TestAddress t("V4Any"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = htonl(INADDR_ANY); - return t; -} - -TestAddress V4Loopback() { - TestAddress t("V4Loopback"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = - htonl(INADDR_LOOPBACK); - return t; -} - -TestAddress V4MappedAny() { - TestAddress t("V4MappedAny"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - inet_pton(AF_INET6, "::ffff:0.0.0.0", - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr); - return t; -} - -TestAddress V4MappedLoopback() { - TestAddress t("V4MappedLoopback"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - inet_pton(AF_INET6, "::ffff:127.0.0.1", - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr); - return t; -} - -TestAddress V4Multicast() { - TestAddress t("V4Multicast"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = - inet_addr(kMulticastAddress); - return t; -} - -TestAddress V4Broadcast() { - TestAddress t("V4Broadcast"); - t.addr.ss_family = AF_INET; - t.addr_len = sizeof(sockaddr_in); - reinterpret_cast<sockaddr_in*>(&t.addr)->sin_addr.s_addr = - htonl(INADDR_BROADCAST); - return t; -} - -TestAddress V6Any() { - TestAddress t("V6Any"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = in6addr_any; - return t; -} - -TestAddress V6Loopback() { - TestAddress t("V6Loopback"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr = in6addr_loopback; - return t; -} - -TestAddress V6Multicast() { - TestAddress t("V6Multicast"); - t.addr.ss_family = AF_INET6; - t.addr_len = sizeof(sockaddr_in6); - EXPECT_EQ( - 1, - inet_pton(AF_INET6, "ff05::1234", - reinterpret_cast<sockaddr_in6*>(&t.addr)->sin6_addr.s6_addr)); - return t; -} - -// Checksum computes the internet checksum of a buffer. -uint16_t Checksum(uint16_t* buf, ssize_t buf_size) { - // Add up the 16-bit values in the buffer. - uint32_t total = 0; - for (unsigned int i = 0; i < buf_size; i += sizeof(*buf)) { - total += *buf; - buf++; - } - - // If buf has an odd size, add the remaining byte. - if (buf_size % 2) { - total += *(reinterpret_cast<unsigned char*>(buf) - 1); - } - - // This carries any bits past the lower 16 until everything fits in 16 bits. - while (total >> 16) { - uint16_t lower = total & 0xffff; - uint16_t upper = total >> 16; - total = lower + upper; - } - - return ~total; -} - -uint16_t IPChecksum(struct iphdr ip) { - return Checksum(reinterpret_cast<uint16_t*>(&ip), sizeof(ip)); -} - -// The pseudo-header defined in RFC 768 for calculating the UDP checksum. -struct udp_pseudo_hdr { - uint32_t srcip; - uint32_t destip; - char zero; - char protocol; - uint16_t udplen; -}; - -uint16_t UDPChecksum(struct iphdr iphdr, struct udphdr udphdr, - const char* payload, ssize_t payload_len) { - struct udp_pseudo_hdr phdr = {}; - phdr.srcip = iphdr.saddr; - phdr.destip = iphdr.daddr; - phdr.zero = 0; - phdr.protocol = IPPROTO_UDP; - phdr.udplen = udphdr.len; - - ssize_t buf_size = sizeof(phdr) + sizeof(udphdr) + payload_len; - char* buf = static_cast<char*>(malloc(buf_size)); - memcpy(buf, &phdr, sizeof(phdr)); - memcpy(buf + sizeof(phdr), &udphdr, sizeof(udphdr)); - memcpy(buf + sizeof(phdr) + sizeof(udphdr), payload, payload_len); - - uint16_t csum = Checksum(reinterpret_cast<uint16_t*>(buf), buf_size); - free(buf); - return csum; -} - -uint16_t ICMPChecksum(struct icmphdr icmphdr, const char* payload, - ssize_t payload_len) { - ssize_t buf_size = sizeof(icmphdr) + payload_len; - char* buf = static_cast<char*>(malloc(buf_size)); - memcpy(buf, &icmphdr, sizeof(icmphdr)); - memcpy(buf + sizeof(icmphdr), payload, payload_len); - - uint16_t csum = Checksum(reinterpret_cast<uint16_t*>(buf), buf_size); - free(buf); - return csum; -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h deleted file mode 100644 index b3ab286b8..000000000 --- a/test/syscalls/linux/socket_test_util.h +++ /dev/null @@ -1,531 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_SOCKET_TEST_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_SOCKET_TEST_UTIL_H_ - -#include <errno.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <netinet/udp.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <functional> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/str_format.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -// Wrapper for socket(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> Socket(int family, int type, int protocol) { - int fd = socket(family, type, protocol); - MaybeSave(); - if (fd < 0) { - return PosixError( - errno, absl::StrFormat("socket(%d, %d, %d)", family, type, protocol)); - } - return FileDescriptor(fd); -} - -// Wrapper for accept(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> Accept(int sockfd, sockaddr* addr, - socklen_t* addrlen) { - int fd = RetryEINTR(accept)(sockfd, addr, addrlen); - MaybeSave(); - if (fd < 0) { - return PosixError( - errno, absl::StrFormat("accept(%d, %p, %p)", sockfd, addr, addrlen)); - } - return FileDescriptor(fd); -} - -// Wrapper for accept4(2) that returns a FileDescriptor. -inline PosixErrorOr<FileDescriptor> Accept4(int sockfd, sockaddr* addr, - socklen_t* addrlen, int flags) { - int fd = RetryEINTR(accept4)(sockfd, addr, addrlen, flags); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, absl::StrFormat("accept4(%d, %p, %p, %#x)", sockfd, - addr, addrlen, flags)); - } - return FileDescriptor(fd); -} - -inline ssize_t SendFd(int fd, void* buf, size_t count, int flags) { - return internal::ApplyFileIoSyscall( - [&](size_t completed) { - return sendto(fd, static_cast<char*>(buf) + completed, - count - completed, flags, nullptr, 0); - }, - count); -} - -PosixErrorOr<struct sockaddr_un> UniqueUnixAddr(bool abstract, int domain); - -// A Creator<T> is a function that attempts to create and return a new T. (This -// is copy/pasted from cloud/gvisor/api/sandbox_util.h and is just duplicated -// here for clarity.) -template <typename T> -using Creator = std::function<PosixErrorOr<std::unique_ptr<T>>()>; - -// A SocketPair represents a pair of socket file descriptors owned by the -// SocketPair. -class SocketPair { - public: - virtual ~SocketPair() = default; - - virtual int first_fd() const = 0; - virtual int second_fd() const = 0; - virtual int release_first_fd() = 0; - virtual int release_second_fd() = 0; - virtual const struct sockaddr* first_addr() const = 0; - virtual const struct sockaddr* second_addr() const = 0; - virtual size_t first_addr_size() const = 0; - virtual size_t second_addr_size() const = 0; - virtual size_t first_addr_len() const = 0; - virtual size_t second_addr_len() const = 0; -}; - -// A FDSocketPair is a SocketPair that consists of only a pair of file -// descriptors. -class FDSocketPair : public SocketPair { - public: - FDSocketPair(int first_fd, int second_fd) - : first_(first_fd), second_(second_fd) {} - FDSocketPair(std::unique_ptr<FileDescriptor> first_fd, - std::unique_ptr<FileDescriptor> second_fd) - : first_(first_fd->release()), second_(second_fd->release()) {} - - int first_fd() const override { return first_.get(); } - int second_fd() const override { return second_.get(); } - int release_first_fd() override { return first_.release(); } - int release_second_fd() override { return second_.release(); } - const struct sockaddr* first_addr() const override { return nullptr; } - const struct sockaddr* second_addr() const override { return nullptr; } - size_t first_addr_size() const override { return 0; } - size_t second_addr_size() const override { return 0; } - size_t first_addr_len() const override { return 0; } - size_t second_addr_len() const override { return 0; } - - private: - FileDescriptor first_; - FileDescriptor second_; -}; - -// CalculateUnixSockAddrLen calculates the length returned by recvfrom(2) and -// recvmsg(2) for Unix sockets. -size_t CalculateUnixSockAddrLen(const char* sun_path); - -// A AddrFDSocketPair is a SocketPair that consists of a pair of file -// descriptors in addition to a pair of socket addresses. -class AddrFDSocketPair : public SocketPair { - public: - AddrFDSocketPair(int first_fd, int second_fd, - const struct sockaddr_un& first_address, - const struct sockaddr_un& second_address) - : first_(first_fd), - second_(second_fd), - first_addr_(to_storage(first_address)), - second_addr_(to_storage(second_address)), - first_len_(CalculateUnixSockAddrLen(first_address.sun_path)), - second_len_(CalculateUnixSockAddrLen(second_address.sun_path)), - first_size_(sizeof(first_address)), - second_size_(sizeof(second_address)) {} - - AddrFDSocketPair(int first_fd, int second_fd, - const struct sockaddr_in& first_address, - const struct sockaddr_in& second_address) - : first_(first_fd), - second_(second_fd), - first_addr_(to_storage(first_address)), - second_addr_(to_storage(second_address)), - first_len_(sizeof(first_address)), - second_len_(sizeof(second_address)), - first_size_(sizeof(first_address)), - second_size_(sizeof(second_address)) {} - - AddrFDSocketPair(int first_fd, int second_fd, - const struct sockaddr_in6& first_address, - const struct sockaddr_in6& second_address) - : first_(first_fd), - second_(second_fd), - first_addr_(to_storage(first_address)), - second_addr_(to_storage(second_address)), - first_len_(sizeof(first_address)), - second_len_(sizeof(second_address)), - first_size_(sizeof(first_address)), - second_size_(sizeof(second_address)) {} - - int first_fd() const override { return first_.get(); } - int second_fd() const override { return second_.get(); } - int release_first_fd() override { return first_.release(); } - int release_second_fd() override { return second_.release(); } - const struct sockaddr* first_addr() const override { - return reinterpret_cast<const struct sockaddr*>(&first_addr_); - } - const struct sockaddr* second_addr() const override { - return reinterpret_cast<const struct sockaddr*>(&second_addr_); - } - size_t first_addr_size() const override { return first_size_; } - size_t second_addr_size() const override { return second_size_; } - size_t first_addr_len() const override { return first_len_; } - size_t second_addr_len() const override { return second_len_; } - - private: - // to_storage coverts a sockaddr_* to a sockaddr_storage. - static struct sockaddr_storage to_storage(const sockaddr_un& addr); - static struct sockaddr_storage to_storage(const sockaddr_in& addr); - static struct sockaddr_storage to_storage(const sockaddr_in6& addr); - - FileDescriptor first_; - FileDescriptor second_; - const struct sockaddr_storage first_addr_; - const struct sockaddr_storage second_addr_; - const size_t first_len_; - const size_t second_len_; - const size_t first_size_; - const size_t second_size_; -}; - -// SyscallSocketPairCreator returns a Creator<SocketPair> that obtains file -// descriptors by invoking the socketpair() syscall. -Creator<SocketPair> SyscallSocketPairCreator(int domain, int type, - int protocol); - -// SyscallSocketCreator returns a Creator<FileDescriptor> that obtains a file -// descriptor by invoking the socket() syscall. -Creator<FileDescriptor> SyscallSocketCreator(int domain, int type, - int protocol); - -// FilesystemBidirectionalBindSocketPairCreator returns a Creator<SocketPair> -// that obtains file descriptors by invoking the bind() and connect() syscalls -// on filesystem paths. Only works for DGRAM sockets. -Creator<SocketPair> FilesystemBidirectionalBindSocketPairCreator(int domain, - int type, - int protocol); - -// AbstractBidirectionalBindSocketPairCreator returns a Creator<SocketPair> that -// obtains file descriptors by invoking the bind() and connect() syscalls on -// abstract namespace paths. Only works for DGRAM sockets. -Creator<SocketPair> AbstractBidirectionalBindSocketPairCreator(int domain, - int type, - int protocol); - -// SocketpairGoferSocketPairCreator returns a Creator<SocketPair> that -// obtains file descriptors by connect() syscalls on two sockets with socketpair -// gofer paths. -Creator<SocketPair> SocketpairGoferSocketPairCreator(int domain, int type, - int protocol); - -// SocketpairGoferFileSocketPairCreator returns a Creator<SocketPair> that -// obtains file descriptors by open() syscalls on socketpair gofer paths. -Creator<SocketPair> SocketpairGoferFileSocketPairCreator(int flags); - -// FilesystemAcceptBindSocketPairCreator returns a Creator<SocketPair> that -// obtains file descriptors by invoking the accept() and bind() syscalls on -// a filesystem path. Only works for STREAM and SEQPACKET sockets. -Creator<SocketPair> FilesystemAcceptBindSocketPairCreator(int domain, int type, - int protocol); - -// AbstractAcceptBindSocketPairCreator returns a Creator<SocketPair> that -// obtains file descriptors by invoking the accept() and bind() syscalls on a -// abstract namespace path. Only works for STREAM and SEQPACKET sockets. -Creator<SocketPair> AbstractAcceptBindSocketPairCreator(int domain, int type, - int protocol); - -// FilesystemUnboundSocketPairCreator returns a Creator<SocketPair> that obtains -// file descriptors by invoking the socket() syscall and generates a filesystem -// path for binding. -Creator<SocketPair> FilesystemUnboundSocketPairCreator(int domain, int type, - int protocol); - -// AbstractUnboundSocketPairCreator returns a Creator<SocketPair> that obtains -// file descriptors by invoking the socket() syscall and generates an abstract -// path for binding. -Creator<SocketPair> AbstractUnboundSocketPairCreator(int domain, int type, - int protocol); - -// TCPAcceptBindSocketPairCreator returns a Creator<SocketPair> that obtains -// file descriptors by invoking the accept() and bind() syscalls on TCP sockets. -Creator<SocketPair> TCPAcceptBindSocketPairCreator(int domain, int type, - int protocol, - bool dual_stack); - -// TCPAcceptBindPersistentListenerSocketPairCreator is like -// TCPAcceptBindSocketPairCreator, except it uses the same listening socket to -// create all SocketPairs. -Creator<SocketPair> TCPAcceptBindPersistentListenerSocketPairCreator( - int domain, int type, int protocol, bool dual_stack); - -// UDPBidirectionalBindSocketPairCreator returns a Creator<SocketPair> that -// obtains file descriptors by invoking the bind() and connect() syscalls on UDP -// sockets. -Creator<SocketPair> UDPBidirectionalBindSocketPairCreator(int domain, int type, - int protocol, - bool dual_stack); - -// UDPUnboundSocketPairCreator returns a Creator<SocketPair> that obtains file -// descriptors by creating UDP sockets. -Creator<SocketPair> UDPUnboundSocketPairCreator(int domain, int type, - int protocol, bool dual_stack); - -// UnboundSocketCreator returns a Creator<FileDescriptor> that obtains a file -// descriptor by creating a socket. -Creator<FileDescriptor> UnboundSocketCreator(int domain, int type, - int protocol); - -// A SocketPairKind couples a human-readable description of a socket pair with -// a function that creates such a socket pair. -struct SocketPairKind { - std::string description; - int domain; - int type; - int protocol; - Creator<SocketPair> creator; - - // Create creates a socket pair of this kind. - PosixErrorOr<std::unique_ptr<SocketPair>> Create() const { return creator(); } -}; - -// A SocketKind couples a human-readable description of a socket with -// a function that creates such a socket. -struct SocketKind { - std::string description; - int domain; - int type; - int protocol; - Creator<FileDescriptor> creator; - - // Create creates a socket pair of this kind. - PosixErrorOr<std::unique_ptr<FileDescriptor>> Create() const { - return creator(); - } -}; - -// A ReversedSocketPair wraps another SocketPair but flips the first and second -// file descriptors. ReversedSocketPair is used to test socket pairs that -// should be symmetric. -class ReversedSocketPair : public SocketPair { - public: - explicit ReversedSocketPair(std::unique_ptr<SocketPair> base) - : base_(std::move(base)) {} - - int first_fd() const override { return base_->second_fd(); } - int second_fd() const override { return base_->first_fd(); } - int release_first_fd() override { return base_->release_second_fd(); } - int release_second_fd() override { return base_->release_first_fd(); } - const struct sockaddr* first_addr() const override { - return base_->second_addr(); - } - const struct sockaddr* second_addr() const override { - return base_->first_addr(); - } - size_t first_addr_size() const override { return base_->second_addr_size(); } - size_t second_addr_size() const override { return base_->first_addr_size(); } - size_t first_addr_len() const override { return base_->second_addr_len(); } - size_t second_addr_len() const override { return base_->first_addr_len(); } - - private: - std::unique_ptr<SocketPair> base_; -}; - -// Reversed returns a SocketPairKind that represents SocketPairs created by -// flipping the file descriptors provided by another SocketPair. -SocketPairKind Reversed(SocketPairKind const& base); - -// IncludeReversals returns a vector<SocketPairKind> that returns all -// SocketPairKinds in `vec` as well as all SocketPairKinds obtained by flipping -// the file descriptors provided by the kinds in `vec`. -std::vector<SocketPairKind> IncludeReversals(std::vector<SocketPairKind> vec); - -// A Middleware is a function wraps a SocketPairKind. -using Middleware = std::function<SocketPairKind(SocketPairKind)>; - -// Reversed returns a SocketPairKind that represents SocketPairs created by -// flipping the file descriptors provided by another SocketPair. -template <typename T> -Middleware SetSockOpt(int level, int optname, T* value) { - return [=](SocketPairKind const& base) { - auto const& creator = base.creator; - return SocketPairKind{ - absl::StrCat("setsockopt(", level, ", ", optname, ", ", *value, ") ", - base.description), - base.domain, base.type, base.protocol, - [creator, level, optname, - value]() -> PosixErrorOr<std::unique_ptr<SocketPair>> { - ASSIGN_OR_RETURN_ERRNO(auto creator_value, creator()); - if (creator_value->first_fd() >= 0) { - RETURN_ERROR_IF_SYSCALL_FAIL(setsockopt( - creator_value->first_fd(), level, optname, value, sizeof(T))); - } - if (creator_value->second_fd() >= 0) { - RETURN_ERROR_IF_SYSCALL_FAIL(setsockopt( - creator_value->second_fd(), level, optname, value, sizeof(T))); - } - return creator_value; - }}; - }; -} - -constexpr int kSockOptOn = 1; -constexpr int kSockOptOff = 0; - -// NoOp returns the same SocketPairKind that it is passed. -SocketPairKind NoOp(SocketPairKind const& base); - -// TransferTest tests that data can be send back and fourth between two -// specified FDs. Note that calls to this function should be wrapped in -// ASSERT_NO_FATAL_FAILURE(). -void TransferTest(int fd1, int fd2); - -// Fills [buf, buf+len) with random bytes. -void RandomizeBuffer(char* buf, size_t len); - -// Base test fixture for tests that operate on pairs of connected sockets. -class SocketPairTest : public ::testing::TestWithParam<SocketPairKind> { - protected: - SocketPairTest() { - // gUnit uses printf, so so will we. - printf("Testing with %s\n", GetParam().description.c_str()); - fflush(stdout); - } - - PosixErrorOr<std::unique_ptr<SocketPair>> NewSocketPair() const { - return GetParam().Create(); - } -}; - -// Base test fixture for tests that operate on simple Sockets. -class SimpleSocketTest : public ::testing::TestWithParam<SocketKind> { - protected: - SimpleSocketTest() { - // gUnit uses printf, so so will we. - printf("Testing with %s\n", GetParam().description.c_str()); - } - - PosixErrorOr<std::unique_ptr<FileDescriptor>> NewSocket() const { - return GetParam().Create(); - } -}; - -SocketKind SimpleSocket(int fam, int type, int proto); - -// Send a buffer of size 'size' to sockets->first_fd(), returning the result of -// sendmsg. -// -// If reader, read from second_fd() until size bytes have been read. -ssize_t SendLargeSendMsg(const std::unique_ptr<SocketPair>& sockets, - size_t size, bool reader); - -// Initializes the given buffer with random data. -void RandomizeBuffer(char* ptr, size_t len); - -enum class AddressFamily { kIpv4 = 1, kIpv6 = 2, kDualStack = 3 }; -enum class SocketType { kUdp = 1, kTcp = 2 }; - -// Returns a PosixError or a port that is available. If 0 is specified as the -// port it will bind port 0 (and allow the kernel to select any free port). -// Otherwise, it will try to bind the specified port and validate that it can be -// used for the requested family and socket type. The final option is -// reuse_addr. This specifies whether SO_REUSEADDR should be applied before a -// bind(2) attempt. SO_REUSEADDR means that sockets in TIME_WAIT states or other -// bound UDP sockets would not cause an error on bind(2). This option should be -// set if subsequent calls to bind on the returned port will also use -// SO_REUSEADDR. -// -// Note: That this test will attempt to bind the ANY address for the respective -// protocol. -PosixErrorOr<int> PortAvailable(int port, AddressFamily family, SocketType type, - bool reuse_addr); - -// FreeAvailablePort is used to return a port that was obtained by using -// the PortAvailable helper with port 0. -PosixError FreeAvailablePort(int port); - -// SendMsg converts a buffer to an iovec and adds it to msg before sending it. -PosixErrorOr<int> SendMsg(int sock, msghdr* msg, char buf[], int buf_size); - -// RecvTimeout calls select on sock with timeout and then calls recv on sock. -PosixErrorOr<int> RecvTimeout(int sock, char buf[], int buf_size, int timeout); - -// RecvMsgTimeout calls select on sock with timeout and then calls recvmsg on -// sock. -PosixErrorOr<int> RecvMsgTimeout(int sock, msghdr* msg, int timeout); - -// RecvNoData checks that no data is receivable on sock. -void RecvNoData(int sock); - -// Base test fixture for tests that apply to all kinds of pairs of connected -// sockets. -using AllSocketPairTest = SocketPairTest; - -struct TestAddress { - std::string description; - sockaddr_storage addr; - socklen_t addr_len; - - explicit TestAddress(std::string description = "") - : description(std::move(description)), addr(), addr_len() {} - - int family() const { return addr.ss_family; } - - // Returns a new TestAddress with specified port. If port is not supported, - // the same TestAddress is returned. - TestAddress WithPort(uint16_t port) const; -}; - -constexpr char kMulticastAddress[] = "224.0.2.1"; -constexpr char kBroadcastAddress[] = "255.255.255.255"; - -TestAddress V4Any(); -TestAddress V4Broadcast(); -TestAddress V4Loopback(); -TestAddress V4MappedAny(); -TestAddress V4MappedLoopback(); -TestAddress V4Multicast(); -TestAddress V6Any(); -TestAddress V6Loopback(); -TestAddress V6Multicast(); - -// Compute the internet checksum of an IP header. -uint16_t IPChecksum(struct iphdr ip); - -// Compute the internet checksum of a UDP header. -uint16_t UDPChecksum(struct iphdr iphdr, struct udphdr udphdr, - const char* payload, ssize_t payload_len); - -// Compute the internet checksum of an ICMP header. -uint16_t ICMPChecksum(struct icmphdr icmphdr, const char* payload, - ssize_t payload_len); - -namespace internal { -PosixErrorOr<int> TryPortAvailable(int port, AddressFamily family, - SocketType type, bool reuse_addr); -} // namespace internal - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_SOCKET_TEST_UTIL_H_ diff --git a/test/syscalls/linux/socket_test_util_impl.cc b/test/syscalls/linux/socket_test_util_impl.cc deleted file mode 100644 index ef661a0e3..000000000 --- a/test/syscalls/linux/socket_test_util_impl.cc +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019 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/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr<int> PortAvailable(int port, AddressFamily family, SocketType type, - bool reuse_addr) { - return internal::TryPortAvailable(port, family, type, reuse_addr); -} - -PosixError FreeAvailablePort(int port) { return NoError(); } - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc deleted file mode 100644 index 591cab3fd..000000000 --- a/test/syscalls/linux/socket_unix.cc +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_unix.h" - -#include <errno.h> -#include <net/if.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -// This file contains tests specific to Unix domain sockets. It does not contain -// tests for UDS control messages. Those belong in socket_unix_cmsg.cc. -// -// This file is a generic socket test file. It must be built with another file -// that provides the test types. - -namespace gvisor { -namespace testing { - -namespace { - -TEST_P(UnixSocketPairTest, InvalidGetSockOpt) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int opt; - socklen_t optlen = sizeof(opt); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, -1, &opt, &optlen), - SyscallFailsWithErrno(ENOPROTOOPT)); -} - -TEST_P(UnixSocketPairTest, BindToBadName) { - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - constexpr char kBadName[] = "/some/path/that/does/not/exist"; - sockaddr_un sockaddr; - sockaddr.sun_family = AF_LOCAL; - memcpy(sockaddr.sun_path, kBadName, sizeof(kBadName)); - - EXPECT_THAT( - bind(pair->first_fd(), reinterpret_cast<struct sockaddr*>(&sockaddr), - sizeof(sockaddr)), - SyscallFailsWithErrno(ENOENT)); -} - -TEST_P(UnixSocketPairTest, BindToBadFamily) { - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - constexpr char kBadName[] = "/some/path/that/does/not/exist"; - sockaddr_un sockaddr; - sockaddr.sun_family = AF_INET; - memcpy(sockaddr.sun_path, kBadName, sizeof(kBadName)); - - EXPECT_THAT( - bind(pair->first_fd(), reinterpret_cast<struct sockaddr*>(&sockaddr), - sizeof(sockaddr)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(UnixSocketPairTest, RecvmmsgTimeoutAfterRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[10]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - char received_data[sizeof(sent_data) * 2]; - std::vector<struct mmsghdr> msgs(2); - std::vector<struct iovec> iovs(msgs.size()); - const int chunk_size = sizeof(received_data) / msgs.size(); - for (size_t i = 0; i < msgs.size(); i++) { - iovs[i].iov_len = chunk_size; - iovs[i].iov_base = &received_data[i * chunk_size]; - msgs[i].msg_hdr.msg_iov = &iovs[i]; - msgs[i].msg_hdr.msg_iovlen = 1; - } - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - struct timespec timeout = {0, 1}; - ASSERT_THAT(RetryEINTR(recvmmsg)(sockets->second_fd(), &msgs[0], msgs.size(), - 0, &timeout), - SyscallSucceedsWithValue(1)); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - EXPECT_EQ(chunk_size, msgs[0].msg_len); -} - -TEST_P(UnixSocketPairTest, TIOCINQSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - if (IsRunningOnGvisor()) { - // TODO(gvisor.dev/issue/273): Inherited host UDS don't support TIOCINQ. - // Skip the test. - int size = -1; - int ret = ioctl(sockets->first_fd(), TIOCINQ, &size); - SKIP_IF(ret == -1 && errno == ENOTTY); - } - - int size = -1; - EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds()); - EXPECT_EQ(size, 0); - - const char some_data[] = "dangerzone"; - ASSERT_THAT( - RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), - SyscallSucceeds()); - EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds()); - EXPECT_EQ(size, sizeof(some_data)); - - // Linux only reports the first message's size, which is wrong. We test for - // the behavior described in the man page. - SKIP_IF(!IsRunningOnGvisor()); - - ASSERT_THAT( - RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), - SyscallSucceeds()); - EXPECT_THAT(ioctl(sockets->first_fd(), TIOCINQ, &size), SyscallSucceeds()); - EXPECT_EQ(size, sizeof(some_data) * 2); -} - -TEST_P(UnixSocketPairTest, TIOCOUTQSucceeds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - if (IsRunningOnGvisor()) { - // TODO(gvisor.dev/issue/273): Inherited host UDS don't support TIOCOUTQ. - // Skip the test. - int size = -1; - int ret = ioctl(sockets->second_fd(), TIOCOUTQ, &size); - SKIP_IF(ret == -1 && errno == ENOTTY); - } - - int size = -1; - EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds()); - EXPECT_EQ(size, 0); - - // Linux reports bogus numbers which are related to its internal allocations. - // We test for the behavior described in the man page. - SKIP_IF(!IsRunningOnGvisor()); - - const char some_data[] = "dangerzone"; - ASSERT_THAT( - RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), - SyscallSucceeds()); - EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds()); - EXPECT_EQ(size, sizeof(some_data)); - - ASSERT_THAT( - RetryEINTR(send)(sockets->second_fd(), &some_data, sizeof(some_data), 0), - SyscallSucceeds()); - EXPECT_THAT(ioctl(sockets->second_fd(), TIOCOUTQ, &size), SyscallSucceeds()); - EXPECT_EQ(size, sizeof(some_data) * 2); -} - -TEST_P(UnixSocketPairTest, NetdeviceIoctlsSucceed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Prepare the request. - struct ifreq ifr; - snprintf(ifr.ifr_name, IFNAMSIZ, "lo"); - - // Check that the ioctl either succeeds or fails with ENODEV. - int err = ioctl(sockets->first_fd(), SIOCGIFINDEX, &ifr); - if (err < 0) { - ASSERT_EQ(errno, ENODEV); - } -} - -TEST_P(UnixSocketPairTest, Shutdown) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - const std::string data = "abc"; - ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), data.size()), - SyscallSucceedsWithValue(data.size())); - - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds()); - ASSERT_THAT(shutdown(sockets->second_fd(), SHUT_RDWR), SyscallSucceeds()); - - // Shutting down a socket does not clear the buffer. - char buf[3]; - ASSERT_THAT(ReadFd(sockets->second_fd(), buf, data.size()), - SyscallSucceedsWithValue(data.size())); - EXPECT_EQ(data, absl::string_view(buf, data.size())); -} - -TEST_P(UnixSocketPairTest, ShutdownRead) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RD), SyscallSucceeds()); - - // When the socket is shutdown for read, read behavior varies between - // different socket types. This is covered by the various ReadOneSideClosed - // test cases. - - // ... and the peer cannot write. - const std::string data = "abc"; - EXPECT_THAT(WriteFd(sockets->second_fd(), data.c_str(), data.size()), - SyscallFailsWithErrno(EPIPE)); - - // ... but the socket can still write. - ASSERT_THAT(WriteFd(sockets->first_fd(), data.c_str(), data.size()), - SyscallSucceedsWithValue(data.size())); - - // ... and the peer can still read. - char buf[3]; - EXPECT_THAT(ReadFd(sockets->second_fd(), buf, data.size()), - SyscallSucceedsWithValue(data.size())); - EXPECT_EQ(data, absl::string_view(buf, data.size())); -} - -TEST_P(UnixSocketPairTest, ShutdownWrite) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_WR), SyscallSucceeds()); - - // When the socket is shutdown for write, it cannot write. - const std::string data = "abc"; - EXPECT_THAT(WriteFd(sockets->first_fd(), data.c_str(), data.size()), - SyscallFailsWithErrno(EPIPE)); - - // ... and the peer read behavior varies between different socket types. This - // is covered by the various ReadOneSideClosed test cases. - - // ... but the peer can still write. - char buf[3]; - ASSERT_THAT(WriteFd(sockets->second_fd(), data.c_str(), data.size()), - SyscallSucceedsWithValue(data.size())); - - // ... and the socket can still read. - EXPECT_THAT(ReadFd(sockets->first_fd(), buf, data.size()), - SyscallSucceedsWithValue(data.size())); - EXPECT_EQ(data, absl::string_view(buf, data.size())); -} - -TEST_P(UnixSocketPairTest, SocketReopenFromProcfs) { - // TODO(gvisor.dev/issue/1624): In VFS1, we return EIO instead of ENXIO (see - // b/122310852). Remove this skip once VFS1 is deleted. - SKIP_IF(IsRunningWithVFS1()); - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Opening a socket pair via /proc/self/fd/X is a ENXIO. - for (const int fd : {sockets->first_fd(), sockets->second_fd()}) { - ASSERT_THAT(Open(absl::StrCat("/proc/self/fd/", fd), O_WRONLY), - PosixErrorIs(ENXIO, ::testing::_)); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix.h b/test/syscalls/linux/socket_unix.h deleted file mode 100644 index 3625cc404..000000000 --- a/test/syscalls/linux/socket_unix.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected unix sockets. -using UnixSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_ diff --git a/test/syscalls/linux/socket_unix_abstract_nonblock.cc b/test/syscalls/linux/socket_unix_abstract_nonblock.cc deleted file mode 100644 index 8bef76b67..000000000 --- a/test/syscalls/linux/socket_unix_abstract_nonblock.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_non_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingAbstractUnixSockets, NonBlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_blocking_local.cc b/test/syscalls/linux/socket_unix_blocking_local.cc deleted file mode 100644 index 77cb8c6d6..000000000 --- a/test/syscalls/linux/socket_unix_blocking_local.cc +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - std::vector<int>{SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}), - ApplyVec<SocketPairKind>( - FilesystemBoundUnixDomainSocketPair, - std::vector<int>{SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM}), - ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - std::vector<int>{SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM})); -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingUnixDomainSockets, BlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_cmsg.cc b/test/syscalls/linux/socket_unix_cmsg.cc deleted file mode 100644 index 22a4ee0d1..000000000 --- a/test/syscalls/linux/socket_unix_cmsg.cc +++ /dev/null @@ -1,1501 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_unix_cmsg.h" - -#include <errno.h> -#include <net/if.h> -#include <stdio.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> -#include <sys/un.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -// This file contains tests for control message in Unix domain sockets. -// -// This file is a generic socket test file. It must be built with another file -// that provides the test types. - -namespace gvisor { -namespace testing { - -namespace { - -TEST_P(UnixSocketPairCmsgTest, BasicFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char received_data[20]; - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, - sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); -} - -TEST_P(UnixSocketPairCmsgTest, BasicTwoFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair1 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - auto pair2 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - int sent_fds[] = {pair1->second_fd(), pair2->second_fd()}; - - ASSERT_NO_FATAL_FAILURE( - SendFDs(sockets->first_fd(), sent_fds, 2, sent_data, sizeof(sent_data))); - - char received_data[20]; - int received_fds[] = {-1, -1}; - - ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 2, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd())); - ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd())); -} - -TEST_P(UnixSocketPairCmsgTest, BasicThreeFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair1 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - auto pair2 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - auto pair3 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - int sent_fds[] = {pair1->second_fd(), pair2->second_fd(), pair3->second_fd()}; - - ASSERT_NO_FATAL_FAILURE( - SendFDs(sockets->first_fd(), sent_fds, 3, sent_data, sizeof(sent_data))); - - char received_data[20]; - int received_fds[] = {-1, -1, -1}; - - ASSERT_NO_FATAL_FAILURE(RecvFDs(sockets->second_fd(), received_fds, 3, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[0], pair1->first_fd())); - ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[1], pair2->first_fd())); - ASSERT_NO_FATAL_FAILURE(TransferTest(received_fds[2], pair3->first_fd())); -} - -TEST_P(UnixSocketPairCmsgTest, BadFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - int sent_fd = -1; - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(sent_fd))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(sent_fd)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &sent_fd, sizeof(sent_fd)); - - struct iovec iov; - iov.iov_base = sent_data; - iov.iov_len = sizeof(sent_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EBADF)); -} - -TEST_P(UnixSocketPairCmsgTest, ShortCmsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - int sent_fd = -1; - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(sent_fd))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = 1; - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - memcpy(CMSG_DATA(cmsg), &sent_fd, sizeof(sent_fd)); - - struct iovec iov; - iov.iov_base = sent_data; - iov.iov_len = sizeof(sent_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EINVAL)); -} - -// BasicFDPassNoSpace starts off by sending a single FD just like BasicFDPass. -// The difference is that when calling recvmsg, no space for FDs is provided, -// only space for the cmsg header. -TEST_P(UnixSocketPairCmsgTest, BasicFDPassNoSpace) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char received_data[20]; - - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(0)); - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_controllen, 0); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -// BasicFDPassNoSpaceMsgCtrunc sends an FD, but does not provide any space to -// receive it. It then verifies that the MSG_CTRUNC flag is set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, BasicFDPassNoSpaceMsgCtrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(0)); - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - char received_data[sizeof(sent_data)]; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_controllen, 0); - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); -} - -// BasicFDPassNullControlMsgCtrunc sends an FD and sets contradictory values for -// msg_controllen and msg_control. msg_controllen is set to the correct size to -// accommodate the FD, but msg_control is set to NULL. In this case, msg_control -// should override msg_controllen. -TEST_P(UnixSocketPairCmsgTest, BasicFDPassNullControlMsgCtrunc) { - // FIXME(gvisor.dev/issue/207): Fix handling of NULL msg_control. - SKIP_IF(IsRunningOnGvisor()); - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - msg.msg_controllen = CMSG_SPACE(1); - - char received_data[sizeof(sent_data)]; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_controllen, 0); - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); -} - -// BasicFDPassNotEnoughSpaceMsgCtrunc sends an FD, but does not provide enough -// space to receive it. It then verifies that the MSG_CTRUNC flag is set in the -// msghdr. -TEST_P(UnixSocketPairCmsgTest, BasicFDPassNotEnoughSpaceMsgCtrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(0) + 1); - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - char received_data[sizeof(sent_data)]; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_controllen, 0); - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); -} - -// BasicThreeFDPassTruncationMsgCtrunc sends three FDs, but only provides enough -// space to receive two of them. It then verifies that the MSG_CTRUNC flag is -// set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, BasicThreeFDPassTruncationMsgCtrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair1 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - auto pair2 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - auto pair3 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - int sent_fds[] = {pair1->second_fd(), pair2->second_fd(), pair3->second_fd()}; - - ASSERT_NO_FATAL_FAILURE( - SendFDs(sockets->first_fd(), sent_fds, 3, sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(2 * sizeof(int))); - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - char received_data[sizeof(sent_data)]; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(2 * sizeof(int))); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_RIGHTS); -} - -// BasicFDPassUnalignedRecv starts off by sending a single FD just like -// BasicFDPass. The difference is that when calling recvmsg, the length of the -// receive data is only aligned on a 4 byte boundary instead of the normal 8. -TEST_P(UnixSocketPairCmsgTest, BasicFDPassUnalignedRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char received_data[20]; - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvSingleFDUnaligned( - sockets->second_fd(), &fd, received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); -} - -// BasicFDPassUnalignedRecvNoMsgTrunc sends one FD and only provides enough -// space to receive just it. (Normally the minimum amount of space one would -// provide would be enough space for two FDs.) It then verifies that the -// MSG_CTRUNC flag is not set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, BasicFDPassUnalignedRecvNoMsgTrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int)) - sizeof(int)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[sizeof(sent_data)] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_flags, 0); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_RIGHTS); -} - -// BasicTwoFDPassUnalignedRecvTruncationMsgTrunc sends two FDs, but only -// provides enough space to receive one of them. It then verifies that the -// MSG_CTRUNC flag is set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, BasicTwoFDPassUnalignedRecvTruncationMsgTrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - int sent_fds[] = {pair->first_fd(), pair->second_fd()}; - - ASSERT_NO_FATAL_FAILURE( - SendFDs(sockets->first_fd(), sent_fds, 2, sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - // CMSG_SPACE rounds up to two FDs, we only want one. - char control[CMSG_SPACE(sizeof(int)) - sizeof(int)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[sizeof(sent_data)] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_RIGHTS); -} - -TEST_P(UnixSocketPairCmsgTest, ConcurrentBasicFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - int sockfd1 = sockets->first_fd(); - auto recv_func = [sockfd1, sent_data]() { - char received_data[20]; - int fd = -1; - RecvSingleFD(sockfd1, &fd, received_data, sizeof(received_data)); - ASSERT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - char buf[20]; - ASSERT_THAT(ReadFd(fd, buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(WriteFd(fd, buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - }; - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->second_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - ScopedThread t(recv_func); - - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(WriteFd(pair->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[20]; - ASSERT_THAT(ReadFd(pair->first_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(sizeof(received_data))); - - t.Join(); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -// FDPassNoRecv checks that the control message can be safely ignored by using -// read(2) instead of recvmsg(2). -TEST_P(UnixSocketPairCmsgTest, FDPassNoRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - // Read while ignoring the passed FD. - char received_data[20]; - ASSERT_THAT( - ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - // Check that the socket still works for reads and writes. - ASSERT_NO_FATAL_FAILURE( - TransferTest(sockets->first_fd(), sockets->second_fd())); -} - -// FDPassInterspersed1 checks that sent control messages cannot be read before -// their associated data has been read. -TEST_P(UnixSocketPairCmsgTest, FDPassInterspersed1) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char written_data[20]; - RandomizeBuffer(written_data, sizeof(written_data)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)), - SyscallSucceedsWithValue(sizeof(written_data))); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - // Check that we don't get a control message, but do get the data. - char received_data[20]; - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)); - EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data))); -} - -// FDPassInterspersed2 checks that sent control messages cannot be read after -// their associated data has been read while ignoring the control message by -// using read(2) instead of recvmsg(2). -TEST_P(UnixSocketPairCmsgTest, FDPassInterspersed2) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char written_data[20]; - RandomizeBuffer(written_data, sizeof(written_data)); - ASSERT_THAT(WriteFd(sockets->first_fd(), written_data, sizeof(written_data)), - SyscallSucceedsWithValue(sizeof(written_data))); - - char received_data[20]; - ASSERT_THAT( - ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - EXPECT_EQ(0, memcmp(written_data, received_data, sizeof(written_data))); -} - -TEST_P(UnixSocketPairCmsgTest, FDPassNotCoalesced) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - auto pair1 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(), - sent_data1, sizeof(sent_data1))); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - auto pair2 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(), - sent_data2, sizeof(sent_data2))); - - char received_data1[sizeof(sent_data1) + sizeof(sent_data2)]; - int received_fd1 = -1; - - RecvSingleFD(sockets->second_fd(), &received_fd1, received_data1, - sizeof(received_data1), sizeof(sent_data1)); - - EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1))); - TransferTest(pair1->first_fd(), pair1->second_fd()); - - char received_data2[sizeof(sent_data1) + sizeof(sent_data2)]; - int received_fd2 = -1; - - RecvSingleFD(sockets->second_fd(), &received_fd2, received_data2, - sizeof(received_data2), sizeof(sent_data2)); - - EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2))); - TransferTest(pair2->first_fd(), pair2->second_fd()); -} - -TEST_P(UnixSocketPairCmsgTest, FDPassPeek) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char peek_data[20]; - int peek_fd = -1; - PeekSingleFD(sockets->second_fd(), &peek_fd, peek_data, sizeof(peek_data)); - EXPECT_EQ(0, memcmp(sent_data, peek_data, sizeof(sent_data))); - TransferTest(peek_fd, pair->first_fd()); - EXPECT_THAT(close(peek_fd), SyscallSucceeds()); - - char received_data[20]; - int received_fd = -1; - RecvSingleFD(sockets->second_fd(), &received_fd, received_data, - sizeof(received_data)); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - TransferTest(received_fd, pair->first_fd()); - EXPECT_THAT(close(received_fd), SyscallSucceeds()); -} - -TEST_P(UnixSocketPairCmsgTest, BasicCredPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - EXPECT_EQ(sent_creds.pid, received_creds.pid); - EXPECT_EQ(sent_creds.uid, received_creds.uid); - EXPECT_EQ(sent_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, SendNullCredsBeforeSoPassCredRecvEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, SendNullCredsAfterSoPassCredRecvEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - SetSoPassCred(sockets->second_fd()); - - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); - - char received_data[20]; - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, SendNullCredsBeforeSoPassCredSendEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->first_fd()); - - char received_data[20]; - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(UnixSocketPairCmsgTest, SendNullCredsAfterSoPassCredSendEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - SetSoPassCred(sockets->first_fd()); - - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); - - char received_data[20]; - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(UnixSocketPairCmsgTest, - SendNullCredsBeforeSoPassCredRecvEndAfterSendEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - SetSoPassCred(sockets->first_fd()); - - ASSERT_NO_FATAL_FAILURE( - SendNullCmsg(sockets->first_fd(), sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, WriteBeforeSoPassCredRecvEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, WriteAfterSoPassCredRecvEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->second_fd()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[20]; - - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, WriteBeforeSoPassCredSendEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - SetSoPassCred(sockets->first_fd()); - - char received_data[20]; - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(UnixSocketPairCmsgTest, WriteAfterSoPassCredSendEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->first_fd()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[20]; - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(UnixSocketPairCmsgTest, WriteBeforeSoPassCredRecvEndAfterSendEnd) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - SetSoPassCred(sockets->first_fd()); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data)), - SyscallSucceedsWithValue(sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixSocketPairCmsgTest, CredPassTruncated) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - struct msghdr msg = {}; - char control[CMSG_SPACE(0) + sizeof(pid_t)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[sizeof(sent_data)] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - EXPECT_EQ(msg.msg_controllen, sizeof(control)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, sizeof(control)); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); - - pid_t pid = 0; - memcpy(&pid, CMSG_DATA(cmsg), sizeof(pid)); - EXPECT_EQ(pid, sent_creds.pid); -} - -// CredPassNoMsgCtrunc passes a full set of credentials. It then verifies that -// receiving the full set does not result in MSG_CTRUNC being set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, CredPassNoMsgCtrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(struct ucred))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[sizeof(sent_data)] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - // The control message should not be truncated. - EXPECT_EQ(msg.msg_flags, 0); - EXPECT_EQ(msg.msg_controllen, sizeof(control)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct ucred))); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); -} - -// CredPassNoSpaceMsgCtrunc passes a full set of credentials. It then receives -// the data without providing space for any credentials and verifies that -// MSG_CTRUNC is set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, CredPassNoSpaceMsgCtrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - struct msghdr msg = {}; - char control[CMSG_SPACE(0)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[sizeof(sent_data)] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - // The control message should be truncated. - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); - EXPECT_EQ(msg.msg_controllen, sizeof(control)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, sizeof(control)); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); -} - -// CredPassTruncatedMsgCtrunc passes a full set of credentials. It then receives -// the data while providing enough space for only the first field of the -// credentials and verifies that MSG_CTRUNC is set in the msghdr. -TEST_P(UnixSocketPairCmsgTest, CredPassTruncatedMsgCtrunc) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - struct msghdr msg = {}; - char control[CMSG_SPACE(0) + sizeof(pid_t)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[sizeof(sent_data)] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - // The control message should be truncated. - EXPECT_EQ(msg.msg_flags, MSG_CTRUNC); - EXPECT_EQ(msg.msg_controllen, sizeof(control)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, sizeof(control)); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); -} - -TEST_P(UnixSocketPairCmsgTest, SoPassCred) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int opt; - socklen_t optLen = sizeof(opt); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), - SyscallSucceeds()); - EXPECT_FALSE(opt); - - optLen = sizeof(opt); - EXPECT_THAT( - getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), - SyscallSucceeds()); - EXPECT_FALSE(opt); - - SetSoPassCred(sockets->first_fd()); - - optLen = sizeof(opt); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), - SyscallSucceeds()); - EXPECT_TRUE(opt); - - optLen = sizeof(opt); - EXPECT_THAT( - getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), - SyscallSucceeds()); - EXPECT_FALSE(opt); - - int zero = 0; - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &zero, - sizeof(zero)), - SyscallSucceeds()); - - optLen = sizeof(opt); - EXPECT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), - SyscallSucceeds()); - EXPECT_FALSE(opt); - - optLen = sizeof(opt); - EXPECT_THAT( - getsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &opt, &optLen), - SyscallSucceeds()); - EXPECT_FALSE(opt); -} - -TEST_P(UnixSocketPairCmsgTest, NoDataCredPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct msghdr msg = {}; - - struct iovec iov; - iov.iov_base = sent_data; - iov.iov_len = sizeof(sent_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - char control[CMSG_SPACE(0)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(0); - - ASSERT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(UnixSocketPairCmsgTest, NoPassCred) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - char received_data[20]; - - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(UnixSocketPairCmsgTest, CredAndFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendCredsAndFD(sockets->first_fd(), sent_creds, - pair->second_fd(), sent_data, - sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - struct ucred received_creds; - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds, - &fd, received_data, - sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - EXPECT_EQ(sent_creds.pid, received_creds.pid); - EXPECT_EQ(sent_creds.uid, received_creds.uid); - EXPECT_EQ(sent_creds.gid, received_creds.gid); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); -} - -TEST_P(UnixSocketPairCmsgTest, FDPassBeforeSoPassCred) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[20]; - struct ucred received_creds; - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds, - &fd, received_data, - sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); -} - -TEST_P(UnixSocketPairCmsgTest, FDPassAfterSoPassCred) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - SetSoPassCred(sockets->second_fd()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char received_data[20]; - struct ucred received_creds; - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvCredsAndFD(sockets->second_fd(), &received_creds, - &fd, received_data, - sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); -} - -TEST_P(UnixSocketPairCmsgTest, CloexecDroppedWhenFDPassed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = ASSERT_NO_ERRNO_AND_VALUE( - UnixDomainSocketPair(SOCK_SEQPACKET | SOCK_CLOEXEC).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char received_data[20]; - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, - sizeof(received_data))); - - EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(0)); -} - -TEST_P(UnixSocketPairCmsgTest, CloexecRecvFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - char received_data[20]; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CMSG_CLOEXEC), - SyscallSucceedsWithValue(sizeof(received_data))); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); - - int fd = -1; - memcpy(&fd, CMSG_DATA(cmsg), sizeof(int)); - - EXPECT_THAT(fcntl(fd, F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); -} - -TEST_P(UnixSocketPairCmsgTest, FDPassAfterSoPassCredWithoutCredSpace) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - SetSoPassCred(sockets->second_fd()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - char control[CMSG_LEN(0)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[20]; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - EXPECT_EQ(msg.msg_controllen, sizeof(control)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, sizeof(control)); - EXPECT_EQ(cmsg->cmsg_level, SOL_SOCKET); - EXPECT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); -} - -// This test will validate that MSG_CTRUNC as an input flag to recvmsg will -// not appear as an output flag on the control message when truncation doesn't -// happen. -TEST_P(UnixSocketPairCmsgTest, MsgCtruncInputIsNoop) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int)) /* we're passing a single fd */]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - char received_data[20]; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, MSG_CTRUNC), - SyscallSucceedsWithValue(sizeof(received_data))); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); - - // Now we should verify that MSG_CTRUNC wasn't set as an output flag. - EXPECT_EQ(msg.msg_flags & MSG_CTRUNC, 0); -} - -TEST_P(UnixSocketPairCmsgTest, FDPassAfterSoPassCredWithoutCredHeaderSpace) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - SetSoPassCred(sockets->second_fd()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - struct msghdr msg = {}; - char control[CMSG_LEN(0) / 2]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - char received_data[20]; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sockets->second_fd(), &msg, 0), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - EXPECT_EQ(msg.msg_controllen, 0); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_cmsg.h b/test/syscalls/linux/socket_unix_cmsg.h deleted file mode 100644 index 431606903..000000000 --- a/test/syscalls/linux/socket_unix_cmsg.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected unix sockets about -// control messages. -using UnixSocketPairCmsgTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_ diff --git a/test/syscalls/linux/socket_unix_dgram.cc b/test/syscalls/linux/socket_unix_dgram.cc deleted file mode 100644 index 5b0844493..000000000 --- a/test/syscalls/linux/socket_unix_dgram.cc +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_unix_dgram.h" - -#include <stdio.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST_P(DgramUnixSocketPairTest, WriteOneSideClosed) { - // FIXME(b/35925052): gVisor datagram sockets return EPIPE instead of - // ECONNREFUSED. - SKIP_IF(IsRunningOnGvisor()); - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - constexpr char kStr[] = "abc"; - ASSERT_THAT(write(sockets->second_fd(), kStr, 3), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -TEST_P(DgramUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int sock = sockets->first_fd(); - int buf_size = 0; - socklen_t buf_size_len = sizeof(buf_size); - ASSERT_THAT(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, &buf_size_len), - SyscallSucceeds()); - int opts; - ASSERT_THAT(opts = fcntl(sock, F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(sock, F_SETFL, opts), SyscallSucceeds()); - - std::vector<char> buf(buf_size / 4); - // Write till the socket buffer is full. - while (RetryEINTR(send)(sock, buf.data(), buf.size(), 0) != -1) { - // Sleep to give linux a chance to move data from the send buffer to the - // receive buffer. - absl::SleepFor(absl::Milliseconds(10)); // 10ms. - } - // The last error should have been EWOULDBLOCK. - ASSERT_EQ(errno, EWOULDBLOCK); - - // Now increase the socket send buffer. - buf_size = buf_size * 2; - ASSERT_THAT( - setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)), - SyscallSucceeds()); - - // The send should succeed again. - ASSERT_THAT(RetryEINTR(send)(sock, buf.data(), buf.size(), 0), - SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_dgram.h b/test/syscalls/linux/socket_unix_dgram.h deleted file mode 100644 index 0764ef85b..000000000 --- a/test/syscalls/linux/socket_unix_dgram.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected dgram unix sockets. -using DgramUnixSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_ diff --git a/test/syscalls/linux/socket_unix_dgram_local.cc b/test/syscalls/linux/socket_unix_dgram_local.cc deleted file mode 100644 index 31d2d5216..000000000 --- a/test/syscalls/linux/socket_unix_dgram_local.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_non_stream.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/socket_unix_dgram.h" -#include "test/syscalls/linux/socket_unix_non_stream.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_DGRAM, SOCK_RAW}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_DGRAM, SOCK_RAW}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_DGRAM, SOCK_RAW}, - List<int>{0, SOCK_NONBLOCK})))); -} - -INSTANTIATE_TEST_SUITE_P( - DgramUnixSockets, DgramUnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - DgramUnixSockets, UnixNonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - DgramUnixSockets, NonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc deleted file mode 100644 index 2db8b68d3..000000000 --- a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of connected non-blocking dgram -// unix sockets. -using NonBlockingDgramUnixSocketPairTest = SocketPairTest; - -TEST_P(NonBlockingDgramUnixSocketPairTest, ReadOneSideClosed) { - if (IsRunningOnGvisor()) { - // FIXME(b/70803293): gVisor datagram sockets return 0 instead of - // EAGAIN. - return; - } - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - char data[10] = {}; - ASSERT_THAT(read(sockets->second_fd(), data, sizeof(data)), - SyscallFailsWithErrno(EAGAIN)); -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingDgramUnixSockets, NonBlockingDgramUnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(std::vector<SocketPairKind>{ - UnixDomainSocketPair(SOCK_DGRAM | SOCK_NONBLOCK), - FilesystemBoundUnixDomainSocketPair(SOCK_DGRAM | SOCK_NONBLOCK), - AbstractBoundUnixDomainSocketPair(SOCK_DGRAM | SOCK_NONBLOCK), - }))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_domain.cc b/test/syscalls/linux/socket_unix_domain.cc deleted file mode 100644 index f7dff8b4d..000000000 --- a/test/syscalls/linux/socket_unix_domain.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_generic.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, AllSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc deleted file mode 100644 index 6700b4d90..000000000 --- a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_non_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVec<SocketPairKind>( - FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingFilesystemUnixSockets, NonBlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_non_stream.cc b/test/syscalls/linux/socket_unix_non_stream.cc deleted file mode 100644 index 884319e1d..000000000 --- a/test/syscalls/linux/socket_unix_non_stream.cc +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_unix_non_stream.h" - -#include <stdio.h> -#include <sys/mman.h> -#include <sys/un.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/memory_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -TEST_P(UnixNonStreamSocketPairTest, RecvMsgTooLarge) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int rcvbuf; - socklen_t length = sizeof(rcvbuf); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &rcvbuf, &length), - SyscallSucceeds()); - - // Make the call larger than the receive buffer. - const int recv_size = 3 * rcvbuf; - - // Write a message that does fit in the receive buffer. - const int write_size = rcvbuf - kPageSize; - - std::vector<char> write_buf(write_size, 'a'); - const int ret = RetryEINTR(write)(sockets->second_fd(), write_buf.data(), - write_buf.size()); - if (ret < 0 && errno == ENOBUFS) { - // NOTE(b/116636318): Linux may stall the write for a long time and - // ultimately return ENOBUFS. Allow this error, since a retry will likely - // result in the same error. - return; - } - ASSERT_THAT(ret, SyscallSucceeds()); - - std::vector<char> recv_buf(recv_size); - - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(sockets->first_fd(), recv_buf.data(), - recv_buf.size(), write_size)); - - recv_buf.resize(write_size); - EXPECT_EQ(recv_buf, write_buf); -} - -// Create a region of anonymous memory of size 'size', which is fragmented in -// FileMem. -// -// ptr contains the start address of the region. The returned vector contains -// all of the mappings to be unmapped when done. -PosixErrorOr<std::vector<Mapping>> CreateFragmentedRegion(const int size, - void** ptr) { - Mapping region; - ASSIGN_OR_RETURN_ERRNO(region, Mmap(nullptr, size, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - - *ptr = region.ptr(); - - // Don't save hundreds of times for all of these mmaps. - DisableSave ds; - - std::vector<Mapping> pages; - - // Map and commit a single page at a time, mapping and committing an unrelated - // page between each call to force FileMem fragmentation. - for (uintptr_t addr = region.addr(); addr < region.endaddr(); - addr += kPageSize) { - Mapping page; - ASSIGN_OR_RETURN_ERRNO( - page, - Mmap(reinterpret_cast<void*>(addr), kPageSize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)); - *reinterpret_cast<volatile char*>(page.ptr()) = 42; - - pages.emplace_back(std::move(page)); - - // Unrelated page elsewhere. - ASSIGN_OR_RETURN_ERRNO(page, - Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0)); - *reinterpret_cast<volatile char*>(page.ptr()) = 42; - - pages.emplace_back(std::move(page)); - } - - // The mappings above have taken ownership of the region. - region.release(); - - return std::move(pages); -} - -// A contiguous iov that is heavily fragmented in FileMem can still be sent -// successfully. See b/115833655. -TEST_P(UnixNonStreamSocketPairTest, FragmentedSendMsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - const int buffer_size = UIO_MAXIOV * kPageSize; - // Extra page for message header overhead. - const int sndbuf = buffer_size + kPageSize; - // N.B. setsockopt(SO_SNDBUF) doubles the passed value. - const int set_sndbuf = sndbuf / 2; - - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, - &set_sndbuf, sizeof(set_sndbuf)), - SyscallSucceeds()); - - int actual_sndbuf = 0; - socklen_t length = sizeof(actual_sndbuf); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, - &actual_sndbuf, &length), - SyscallSucceeds()); - - if (actual_sndbuf != sndbuf) { - // Unable to get the sndbuf we want. - // - // N.B. At minimum, the socketpair gofer should provide a socket that is - // already the correct size. - // - // TODO(b/35921550): When internal UDS support SO_SNDBUF, we can assert that - // we always get the right SO_SNDBUF on gVisor. - GTEST_SKIP() << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf; - } - - // Create a contiguous region of memory of 2*UIO_MAXIOV*PAGE_SIZE. We'll call - // sendmsg with a single iov, but the goal is to get the sentry to split this - // into > UIO_MAXIOV iovs when calling the kernel. - void* ptr; - std::vector<Mapping> pages = - ASSERT_NO_ERRNO_AND_VALUE(CreateFragmentedRegion(buffer_size, &ptr)); - - struct iovec iov = {}; - iov.iov_base = ptr; - iov.iov_len = buffer_size; - - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - // NOTE(b/116636318,b/115833655): Linux has poor behavior in the presence of - // physical memory fragmentation. As a result, this may stall for a long time - // and ultimately return ENOBUFS. Allow this error, since it means that we - // made it to the host kernel and started the sendmsg. - EXPECT_THAT(RetryEINTR(sendmsg)(sockets->first_fd(), &msg, 0), - AnyOf(SyscallSucceedsWithValue(buffer_size), - SyscallFailsWithErrno(ENOBUFS))); -} - -// A contiguous iov that is heavily fragmented in FileMem can still be received -// into successfully. Regression test for b/115833655. -TEST_P(UnixNonStreamSocketPairTest, FragmentedRecvMsg) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - const int buffer_size = UIO_MAXIOV * kPageSize; - // Extra page for message header overhead. - const int sndbuf = buffer_size + kPageSize; - // N.B. setsockopt(SO_SNDBUF) doubles the passed value. - const int set_sndbuf = sndbuf / 2; - - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, - &set_sndbuf, sizeof(set_sndbuf)), - SyscallSucceeds()); - - int actual_sndbuf = 0; - socklen_t length = sizeof(actual_sndbuf); - ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, - &actual_sndbuf, &length), - SyscallSucceeds()); - - if (actual_sndbuf != sndbuf) { - // Unable to get the sndbuf we want. - // - // N.B. At minimum, the socketpair gofer should provide a socket that is - // already the correct size. - // - // TODO(b/35921550): When internal UDS support SO_SNDBUF, we can assert that - // we always get the right SO_SNDBUF on gVisor. - GTEST_SKIP() << "SO_SNDBUF = " << actual_sndbuf << ", want " << sndbuf; - } - - std::vector<char> write_buf(buffer_size, 'a'); - const int ret = RetryEINTR(write)(sockets->first_fd(), write_buf.data(), - write_buf.size()); - if (ret < 0 && errno == ENOBUFS) { - // NOTE(b/116636318): Linux may stall the write for a long time and - // ultimately return ENOBUFS. Allow this error, since a retry will likely - // result in the same error. - return; - } - ASSERT_THAT(ret, SyscallSucceeds()); - - // Create a contiguous region of memory of 2*UIO_MAXIOV*PAGE_SIZE. We'll call - // sendmsg with a single iov, but the goal is to get the sentry to split this - // into > UIO_MAXIOV iovs when calling the kernel. - void* ptr; - std::vector<Mapping> pages = - ASSERT_NO_ERRNO_AND_VALUE(CreateFragmentedRegion(buffer_size, &ptr)); - - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg( - sockets->second_fd(), reinterpret_cast<char*>(ptr), buffer_size)); - - EXPECT_EQ(0, memcmp(write_buf.data(), ptr, buffer_size)); -} - -TEST_P(UnixNonStreamSocketPairTest, SendTimeout) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - const int buf_size = 5 * kPageSize; - EXPECT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF, &buf_size, - sizeof(buf_size)), - SyscallSucceeds()); - EXPECT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_RCVBUF, &buf_size, - sizeof(buf_size)), - SyscallSucceeds()); - - // The buffer size should be big enough to avoid many iterations in the next - // loop. Otherwise, this will slow down cooperative_save tests. - std::vector<char> buf(kPageSize); - for (;;) { - int ret; - ASSERT_THAT( - ret = RetryEINTR(send)(sockets->first_fd(), buf.data(), buf.size(), 0), - ::testing::AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(EAGAIN))); - if (ret == -1) { - break; - } - } -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_non_stream.h b/test/syscalls/linux/socket_unix_non_stream.h deleted file mode 100644 index 7478ab172..000000000 --- a/test/syscalls/linux/socket_unix_non_stream.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected non-stream -// unix-domain sockets. -using UnixNonStreamSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_ diff --git a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc deleted file mode 100644 index fddcdf1c5..000000000 --- a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_non_stream_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>(UnixDomainSocketPair, - std::vector<int>{SOCK_DGRAM, SOCK_SEQPACKET}), - ApplyVec<SocketPairKind>(FilesystemBoundUnixDomainSocketPair, - std::vector<int>{SOCK_DGRAM, SOCK_SEQPACKET}), - ApplyVec<SocketPairKind>(AbstractBoundUnixDomainSocketPair, - std::vector<int>{SOCK_DGRAM, SOCK_SEQPACKET})); -} - -INSTANTIATE_TEST_SUITE_P( - BlockingNonStreamUnixSockets, BlockingNonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_pair.cc b/test/syscalls/linux/socket_unix_pair.cc deleted file mode 100644 index 85999db04..000000000 --- a/test/syscalls/linux/socket_unix_pair.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/socket_unix.h" -#include "test/syscalls/linux/socket_unix_cmsg.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>(ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK}))); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnixSocketPairCmsgTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_pair_nonblock.cc b/test/syscalls/linux/socket_unix_pair_nonblock.cc deleted file mode 100644 index 281410a9a..000000000 --- a/test/syscalls/linux/socket_unix_pair_nonblock.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_non_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET}, - List<int>{SOCK_NONBLOCK})); -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingUnixSockets, NonBlockingSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_seqpacket.cc b/test/syscalls/linux/socket_unix_seqpacket.cc deleted file mode 100644 index d6e7031c0..000000000 --- a/test/syscalls/linux/socket_unix_seqpacket.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/socket_unix_seqpacket.h" - -#include <stdio.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST_P(SeqpacketUnixSocketPairTest, WriteOneSideClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - constexpr char kStr[] = "abc"; - ASSERT_THAT(write(sockets->second_fd(), kStr, 3), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(SeqpacketUnixSocketPairTest, ReadOneSideClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - char data[10] = {}; - ASSERT_THAT(read(sockets->second_fd(), data, sizeof(data)), - SyscallSucceedsWithValue(0)); -} - -TEST_P(SeqpacketUnixSocketPairTest, Sendto) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = {}; - addr.sun_family = AF_UNIX; - constexpr char kPath[] = "\0nonexistent"; - memcpy(addr.sun_path, kPath, sizeof(kPath)); - - constexpr char kStr[] = "abc"; - ASSERT_THAT(sendto(sockets->second_fd(), kStr, 3, 0, (struct sockaddr*)&addr, - sizeof(addr)), - SyscallSucceedsWithValue(3)); - - char data[10] = {}; - ASSERT_THAT(read(sockets->first_fd(), data, sizeof(data)), - SyscallSucceedsWithValue(3)); -} - -TEST_P(SeqpacketUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int sock = sockets->first_fd(); - int buf_size = 0; - socklen_t buf_size_len = sizeof(buf_size); - ASSERT_THAT(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, &buf_size_len), - SyscallSucceeds()); - int opts; - ASSERT_THAT(opts = fcntl(sock, F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(sock, F_SETFL, opts), SyscallSucceeds()); - - std::vector<char> buf(buf_size / 4); - // Write till the socket buffer is full. - while (RetryEINTR(send)(sock, buf.data(), buf.size(), 0) != -1) { - // Sleep to give linux a chance to move data from the send buffer to the - // receive buffer. - absl::SleepFor(absl::Milliseconds(10)); // 10ms. - } - // The last error should have been EWOULDBLOCK. - ASSERT_EQ(errno, EWOULDBLOCK); - - // Now increase the socket send buffer. - buf_size = buf_size * 2; - ASSERT_THAT( - setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)), - SyscallSucceeds()); - - // Skip test if the setsockopt didn't increase the sendbuf. This happens for - // tests where the socket is a host fd where gVisor does not permit increasing - // send buffer size. - int new_buf_size = 0; - buf_size_len = sizeof(new_buf_size); - ASSERT_THAT( - getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &new_buf_size, &buf_size_len), - SyscallSucceeds()); - if (IsRunningOnGvisor() && (new_buf_size <= buf_size)) { - GTEST_SKIP() << "Skipping test new send buffer size " << new_buf_size - << " is the same as the value before setsockopt, " - << " socket is probably a host backed socket." << std ::endl; - } - // send should succeed again. - ASSERT_THAT(RetryEINTR(send)(sock, buf.data(), buf.size(), 0), - SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_seqpacket.h b/test/syscalls/linux/socket_unix_seqpacket.h deleted file mode 100644 index 30d9b9edf..000000000 --- a/test/syscalls/linux/socket_unix_seqpacket.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_ -#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_ - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// Test fixture for tests that apply to pairs of connected seqpacket unix -// sockets. -using SeqpacketUnixSocketPairTest = SocketPairTest; - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_ diff --git a/test/syscalls/linux/socket_unix_seqpacket_local.cc b/test/syscalls/linux/socket_unix_seqpacket_local.cc deleted file mode 100644 index 69a5f150d..000000000 --- a/test/syscalls/linux/socket_unix_seqpacket_local.cc +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_non_stream.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/socket_unix_non_stream.h" -#include "test/syscalls/linux/socket_unix_seqpacket.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})))); -} - -INSTANTIATE_TEST_SUITE_P( - SeqpacketUnixSockets, NonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - SeqpacketUnixSockets, SeqpacketUnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -INSTANTIATE_TEST_SUITE_P( - SeqpacketUnixSockets, UnixNonStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc deleted file mode 100644 index 3ff810914..000000000 --- a/test/syscalls/linux/socket_unix_stream.cc +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <poll.h> -#include <stdio.h> -#include <sys/un.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of connected stream unix sockets. -using StreamUnixSocketPairTest = SocketPairTest; - -TEST_P(StreamUnixSocketPairTest, WriteOneSideClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - constexpr char kStr[] = "abc"; - ASSERT_THAT(write(sockets->second_fd(), kStr, 3), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(StreamUnixSocketPairTest, ReadOneSideClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - char data[10] = {}; - ASSERT_THAT(read(sockets->second_fd(), data, sizeof(data)), - SyscallSucceedsWithValue(0)); -} - -TEST_P(StreamUnixSocketPairTest, RecvmsgOneSideClosed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Set timeout so that it will not wait for ever. - struct timeval tv { - .tv_sec = 0, .tv_usec = 10 - }; - EXPECT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, - sizeof(tv)), - SyscallSucceeds()); - - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - - char received_data[10] = {}; - struct iovec iov; - iov.iov_base = received_data; - iov.iov_len = sizeof(received_data); - struct msghdr msg = {}; - msg.msg_flags = -1; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(recvmsg(sockets->second_fd(), &msg, MSG_WAITALL), - SyscallSucceedsWithValue(0)); -} - -TEST_P(StreamUnixSocketPairTest, ReadOneSideClosedWithUnreadData) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char buf[10] = {}; - ASSERT_THAT(RetryEINTR(write)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - ASSERT_THAT(shutdown(sockets->first_fd(), SHUT_RDWR), SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)), - SyscallSucceedsWithValue(0)); - - ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(read)(sockets->second_fd(), buf, sizeof(buf)), - SyscallFailsWithErrno(ECONNRESET)); -} - -TEST_P(StreamUnixSocketPairTest, Sendto) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = {}; - addr.sun_family = AF_UNIX; - constexpr char kPath[] = "\0nonexistent"; - memcpy(addr.sun_path, kPath, sizeof(kPath)); - - constexpr char kStr[] = "abc"; - ASSERT_THAT(sendto(sockets->second_fd(), kStr, 3, 0, (struct sockaddr*)&addr, - sizeof(addr)), - SyscallFailsWithErrno(EISCONN)); -} - -TEST_P(StreamUnixSocketPairTest, SetAndGetSocketLinger) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct linger sl = {1, 5}; - EXPECT_THAT( - setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceedsWithValue(0)); - - struct linger got_linger = {}; - socklen_t length = sizeof(sl); - EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, - &got_linger, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got_linger)); - EXPECT_EQ(0, memcmp(&got_linger, &sl, length)); -} - -TEST_P(StreamUnixSocketPairTest, GetSocketAcceptConn) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT( - getsockopt(sockets->first_fd(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceedsWithValue(0)); - - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); -} - -TEST_P(StreamUnixSocketPairTest, SetSocketSendBuf) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - auto s = sockets->first_fd(); - int max = 0; - int min = 0; - { - // Discover maxmimum buffer size by setting to a really large value. - constexpr int kRcvBufSz = INT_MAX; - ASSERT_THAT( - setsockopt(s, SOL_SOCKET, SO_SNDBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - max = 0; - socklen_t max_len = sizeof(max); - ASSERT_THAT(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &max, &max_len), - SyscallSucceeds()); - } - - { - // Discover minimum buffer size by setting it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT( - setsockopt(s, SOL_SOCKET, SO_SNDBUF, &kRcvBufSz, sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &min, &min_len), - SyscallSucceeds()); - } - - int quarter_sz = min + (max - min) / 4; - ASSERT_THAT( - setsockopt(s, SOL_SOCKET, SO_SNDBUF, &quarter_sz, sizeof(quarter_sz)), - SyscallSucceeds()); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &val, &val_len), - SyscallSucceeds()); - - // Linux doubles the value set by SO_SNDBUF/SO_SNDBUF. - quarter_sz *= 2; - ASSERT_EQ(quarter_sz, val); -} - -TEST_P(StreamUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - int sock = sockets->first_fd(); - int buf_size = 0; - socklen_t buf_size_len = sizeof(buf_size); - ASSERT_THAT(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, &buf_size_len), - SyscallSucceeds()); - int opts; - ASSERT_THAT(opts = fcntl(sock, F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(sock, F_SETFL, opts), SyscallSucceeds()); - - std::vector<char> buf(buf_size / 4); - // Write till the socket buffer is full. - while (RetryEINTR(send)(sock, buf.data(), buf.size(), 0) != -1) { - // Sleep to give linux a chance to move data from the send buffer to the - // receive buffer. - absl::SleepFor(absl::Milliseconds(10)); // 10ms. - } - // The last error should have been EWOULDBLOCK. - ASSERT_EQ(errno, EWOULDBLOCK); - - // Now increase the socket send buffer. - buf_size = buf_size * 2; - ASSERT_THAT( - setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)), - SyscallSucceeds()); - - // The send should succeed again. - ASSERT_THAT(RetryEINTR(send)(sock, buf.data(), buf.size(), 0), - SyscallSucceeds()); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, StreamUnixSocketPairTest, - ::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>(UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>(FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK})))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_stream_blocking_local.cc deleted file mode 100644 index 8429bd429..000000000 --- a/test/syscalls/linux/socket_unix_stream_blocking_local.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_stream_blocking.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return { - UnixDomainSocketPair(SOCK_STREAM), - FilesystemBoundUnixDomainSocketPair(SOCK_STREAM), - AbstractBoundUnixDomainSocketPair(SOCK_STREAM), - }; -} - -INSTANTIATE_TEST_SUITE_P( - BlockingStreamUnixSockets, BlockingStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream_local.cc b/test/syscalls/linux/socket_unix_stream_local.cc deleted file mode 100644 index a7e3449a9..000000000 --- a/test/syscalls/linux/socket_unix_stream_local.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <vector> - -#include "test/syscalls/linux/socket_stream.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK}))); -} - -INSTANTIATE_TEST_SUITE_P( - StreamUnixSockets, StreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc deleted file mode 100644 index 4b763c8e2..000000000 --- a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#include <vector> - -#include "test/syscalls/linux/socket_stream_nonblock.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/syscalls/linux/unix_domain_socket_test_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -std::vector<SocketPairKind> GetSocketPairs() { - return { - UnixDomainSocketPair(SOCK_STREAM | SOCK_NONBLOCK), - FilesystemBoundUnixDomainSocketPair(SOCK_STREAM | SOCK_NONBLOCK), - AbstractBoundUnixDomainSocketPair(SOCK_STREAM | SOCK_NONBLOCK), - }; -} - -INSTANTIATE_TEST_SUITE_P( - NonBlockingStreamUnixSockets, NonBlockingStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(GetSocketPairs()))); - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_abstract.cc b/test/syscalls/linux/socket_unix_unbound_abstract.cc deleted file mode 100644 index 8b1762000..000000000 --- a/test/syscalls/linux/socket_unix_unbound_abstract.cc +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of unbound abstract unix sockets. -using UnboundAbstractUnixSocketPairTest = SocketPairTest; - -TEST_P(UnboundAbstractUnixSocketPairTest, AddressAfterNull) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = - *reinterpret_cast<const struct sockaddr_un*>(sockets->first_addr()); - ASSERT_EQ(addr.sun_path[sizeof(addr.sun_path) - 1], 0); - SKIP_IF(addr.sun_path[sizeof(addr.sun_path) - 2] != 0 || - addr.sun_path[sizeof(addr.sun_path) - 3] != 0); - - addr.sun_path[sizeof(addr.sun_path) - 2] = 'a'; - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(bind(sockets->second_fd(), - reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceeds()); -} - -TEST_P(UnboundAbstractUnixSocketPairTest, ShortAddressNotExtended) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = - *reinterpret_cast<const struct sockaddr_un*>(sockets->first_addr()); - ASSERT_EQ(addr.sun_path[sizeof(addr.sun_path) - 1], 0); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size() - 1), - SyscallSucceeds()); - - ASSERT_THAT(bind(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(UnboundAbstractUnixSocketPairTest, BindNothing) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - ASSERT_THAT(bind(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceeds()); -} - -TEST_P(UnboundAbstractUnixSocketPairTest, GetSockNameFullLength) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - sockaddr_storage addr = {}; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT(getsockname(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(&addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, sockets->first_addr_size()); -} - -TEST_P(UnboundAbstractUnixSocketPairTest, GetSockNamePartialLength) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size() - 1), - SyscallSucceeds()); - - sockaddr_storage addr = {}; - socklen_t addr_len = sizeof(addr); - ASSERT_THAT(getsockname(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(&addr), &addr_len), - SyscallSucceeds()); - EXPECT_EQ(addr_len, sockets->first_addr_size() - 1); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnboundAbstractUnixSocketPairTest, - ::testing::ValuesIn(ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_SEQPACKET, - SOCK_DGRAM}, - List<int>{0, SOCK_NONBLOCK})))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_dgram.cc b/test/syscalls/linux/socket_unix_unbound_dgram.cc deleted file mode 100644 index 907dca0f1..000000000 --- a/test/syscalls/linux/socket_unix_unbound_dgram.cc +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/socket.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of unbound dgram unix sockets. -using UnboundDgramUnixSocketPairTest = SocketPairTest; - -TEST_P(UnboundDgramUnixSocketPairTest, BindConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(UnboundDgramUnixSocketPairTest, SelfConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(connect(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(UnboundDgramUnixSocketPairTest, DoubleConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); -} - -TEST_P(UnboundDgramUnixSocketPairTest, GetRemoteAddress) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - socklen_t addressLength = sockets->first_addr_size(); - struct sockaddr_storage address = {}; - ASSERT_THAT(getpeername(sockets->second_fd(), (struct sockaddr*)(&address), - &addressLength), - SyscallSucceeds()); - EXPECT_EQ( - 0, memcmp(&address, sockets->first_addr(), sockets->first_addr_size())); -} - -TEST_P(UnboundDgramUnixSocketPairTest, Sendto) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(sendto(sockets->second_fd(), sent_data, sizeof(sent_data), 0, - sockets->first_addr(), sockets->first_addr_size()), - SyscallSucceedsWithValue(sizeof(sent_data))); - - char received_data[sizeof(sent_data)]; - ASSERT_THAT(ReadFd(sockets->first_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(sizeof(received_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); -} - -TEST_P(UnboundDgramUnixSocketPairTest, ZeroWriteAllowed) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - ASSERT_THAT(connect(sockets->second_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - char sent_data[3]; - // Send a zero length packet. - ASSERT_THAT(write(sockets->second_fd(), sent_data, 0), - SyscallSucceedsWithValue(0)); - // Receive the packet. - char received_data[sizeof(sent_data)]; - ASSERT_THAT(read(sockets->first_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(0)); -} - -TEST_P(UnboundDgramUnixSocketPairTest, Listen) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(listen(sockets->first_fd(), 0), SyscallFailsWithErrno(ENOTSUP)); -} - -TEST_P(UnboundDgramUnixSocketPairTest, Accept) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - ASSERT_THAT(accept(sockets->first_fd(), nullptr, nullptr), - SyscallFailsWithErrno(ENOTSUP)); -} - -TEST_P(UnboundDgramUnixSocketPairTest, SendtoWithoutConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - char data = 'a'; - ASSERT_THAT( - RetryEINTR(sendto)(sockets->second_fd(), &data, sizeof(data), 0, - sockets->first_addr(), sockets->first_addr_size()), - SyscallSucceedsWithValue(sizeof(data))); -} - -TEST_P(UnboundDgramUnixSocketPairTest, SendtoWithoutConnectPassCreds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - SetSoPassCred(sockets->first_fd()); - char data = 'a'; - ASSERT_THAT( - RetryEINTR(sendto)(sockets->second_fd(), &data, sizeof(data), 0, - sockets->first_addr(), sockets->first_addr_size()), - SyscallSucceedsWithValue(sizeof(data))); - ucred creds; - creds.pid = -1; - char buf[sizeof(data) + 1]; - ASSERT_NO_FATAL_FAILURE( - RecvCreds(sockets->first_fd(), &creds, buf, sizeof(buf), sizeof(data))); - EXPECT_EQ(0, memcmp(&data, buf, sizeof(data))); - EXPECT_THAT(getpid(), SyscallSucceedsWithValue(creds.pid)); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnboundDgramUnixSocketPairTest, - ::testing::ValuesIn(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>(FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_DGRAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_DGRAM}, - List<int>{0, SOCK_NONBLOCK}))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_filesystem.cc b/test/syscalls/linux/socket_unix_unbound_filesystem.cc deleted file mode 100644 index a035fb095..000000000 --- a/test/syscalls/linux/socket_unix_unbound_filesystem.cc +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <stdio.h> -#include <sys/un.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 fixture for tests that apply to pairs of unbound filesystem unix -// sockets. -using UnboundFilesystemUnixSocketPairTest = SocketPairTest; - -TEST_P(UnboundFilesystemUnixSocketPairTest, AddressAfterNull) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - struct sockaddr_un addr = - *reinterpret_cast<const struct sockaddr_un*>(sockets->first_addr()); - ASSERT_EQ(addr.sun_path[sizeof(addr.sun_path) - 1], 0); - SKIP_IF(addr.sun_path[sizeof(addr.sun_path) - 2] != 0 || - addr.sun_path[sizeof(addr.sun_path) - 3] != 0); - - addr.sun_path[sizeof(addr.sun_path) - 2] = 'a'; - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - ASSERT_THAT(bind(sockets->second_fd(), - reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(UnboundFilesystemUnixSocketPairTest, GetSockNameLength) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - sockaddr_storage got_addr = {}; - socklen_t got_addr_len = sizeof(got_addr); - ASSERT_THAT( - getsockname(sockets->first_fd(), - reinterpret_cast<struct sockaddr*>(&got_addr), &got_addr_len), - SyscallSucceeds()); - - sockaddr_un want_addr = - *reinterpret_cast<const struct sockaddr_un*>(sockets->first_addr()); - - EXPECT_EQ(got_addr_len, - strlen(want_addr.sun_path) + 1 + sizeof(want_addr.sun_family)); -} - -TEST_P(UnboundFilesystemUnixSocketPairTest, OpenSocketWithTruncate) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - const struct sockaddr_un *addr = - reinterpret_cast<const struct sockaddr_un *>(sockets->first_addr()); - EXPECT_THAT(chmod(addr->sun_path, 0777), SyscallSucceeds()); - EXPECT_THAT(open(addr->sun_path, O_RDONLY | O_TRUNC), - SyscallFailsWithErrno(ENXIO)); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnboundFilesystemUnixSocketPairTest, - ::testing::ValuesIn(ApplyVec<SocketPairKind>( - FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM, SOCK_SEQPACKET, - SOCK_DGRAM}, - List<int>{0, SOCK_NONBLOCK})))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc deleted file mode 100644 index cb99030f5..000000000 --- a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of unbound seqpacket unix sockets. -using UnboundUnixSeqpacketSocketPairTest = SocketPairTest; - -TEST_P(UnboundUnixSeqpacketSocketPairTest, SendtoWithoutConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - char data = 'a'; - ASSERT_THAT(sendto(sockets->second_fd(), &data, sizeof(data), 0, - sockets->first_addr(), sockets->first_addr_size()), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(UnboundUnixSeqpacketSocketPairTest, SendtoWithoutConnectIgnoresAddr) { - // FIXME(b/68223466): gVisor tries to find /foo/bar and thus returns ENOENT. - if (IsRunningOnGvisor()) { - return; - } - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - // Even a bogus address is completely ignored. - constexpr char kPath[] = "/foo/bar"; - - // Sanity check that kPath doesn't exist. - struct stat s; - ASSERT_THAT(stat(kPath, &s), SyscallFailsWithErrno(ENOENT)); - - struct sockaddr_un addr = {}; - addr.sun_family = AF_UNIX; - memcpy(addr.sun_path, kPath, sizeof(kPath)); - - char data = 'a'; - ASSERT_THAT( - sendto(sockets->second_fd(), &data, sizeof(data), 0, - reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(ENOTCONN)); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnboundUnixSeqpacketSocketPairTest, - ::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>( - FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_SEQPACKET}, - List<int>{0, SOCK_NONBLOCK})))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/socket_unix_unbound_stream.cc b/test/syscalls/linux/socket_unix_unbound_stream.cc deleted file mode 100644 index f185dded3..000000000 --- a/test/syscalls/linux/socket_unix_unbound_stream.cc +++ /dev/null @@ -1,733 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdio.h> -#include <sys/un.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/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Test fixture for tests that apply to pairs of connected unix stream sockets. -using UnixStreamSocketPairTest = SocketPairTest; - -// FDPassPartialRead checks that sent control messages cannot be read after -// any of their associated data has been read while ignoring the control message -// by using read(2) instead of recvmsg(2). -TEST_P(UnixStreamSocketPairTest, FDPassPartialRead) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data))); - - char received_data[sizeof(sent_data) / 2]; - ASSERT_THAT( - ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(received_data))); - - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)); - EXPECT_EQ(0, memcmp(sent_data + sizeof(received_data), received_data, - sizeof(received_data))); -} - -TEST_P(UnixStreamSocketPairTest, FDPassCoalescedRead) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - auto pair1 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(), - sent_data1, sizeof(sent_data1))); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - auto pair2 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(), - sent_data2, sizeof(sent_data2))); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - ASSERT_THAT( - ReadFd(sockets->second_fd(), received_data, sizeof(received_data)), - SyscallSucceedsWithValue(sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), - sizeof(sent_data2))); -} - -// ZeroLengthMessageFDDiscarded checks that control messages associated with -// zero length messages are discarded. -TEST_P(UnixStreamSocketPairTest, ZeroLengthMessageFDDiscarded) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - // Zero length arrays are invalid in ISO C++, so allocate one of size 1 and - // send a length of 0. - char sent_data1[1] = {}; - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE( - SendSingleFD(sockets->first_fd(), pair->second_fd(), sent_data1, 0)); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - char received_data[sizeof(sent_data2)] = {}; - - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data)); - EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(received_data))); -} - -// FDPassCoalescedRecv checks that control messages not in the first message are -// preserved in a coalesced recv. -TEST_P(UnixStreamSocketPairTest, FDPassCoalescedRecv) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data, sizeof(sent_data) / 2), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data + sizeof(sent_data) / 2, - sizeof(sent_data) / 2)); - - char received_data[sizeof(sent_data)]; - - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, - sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); -} - -// ReadsNotCoalescedAfterFDPass checks that messages after a message containing -// an FD control message are not coalesced. -TEST_P(UnixStreamSocketPairTest, ReadsNotCoalescedAfterFDPass) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair->second_fd(), - sent_data, sizeof(sent_data) / 2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data + sizeof(sent_data) / 2, - sizeof(sent_data) / 2), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - - char received_data[sizeof(sent_data)]; - - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, - sizeof(received_data), - sizeof(sent_data) / 2)); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair->first_fd())); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(sent_data) / 2)); - - EXPECT_EQ(0, memcmp(sent_data + sizeof(sent_data) / 2, received_data, - sizeof(sent_data) / 2)); -} - -// FDPassNotCombined checks that FD control messages are not combined in a -// coalesced read. -TEST_P(UnixStreamSocketPairTest, FDPassNotCombined) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - auto pair1 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair1->second_fd(), - sent_data, sizeof(sent_data) / 2)); - - auto pair2 = - ASSERT_NO_ERRNO_AND_VALUE(UnixDomainSocketPair(SOCK_SEQPACKET).Create()); - - ASSERT_NO_FATAL_FAILURE(SendSingleFD(sockets->first_fd(), pair2->second_fd(), - sent_data + sizeof(sent_data) / 2, - sizeof(sent_data) / 2)); - - char received_data[sizeof(sent_data)]; - - int fd = -1; - ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, - sizeof(received_data), - sizeof(sent_data) / 2)); - - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair1->first_fd())); - - EXPECT_THAT(close(fd), SyscallSucceeds()); - fd = -1; - - ASSERT_NO_FATAL_FAILURE(RecvSingleFD(sockets->second_fd(), &fd, received_data, - sizeof(received_data), - sizeof(sent_data) / 2)); - - EXPECT_EQ(0, memcmp(sent_data + sizeof(sent_data) / 2, received_data, - sizeof(sent_data) / 2)); - - ASSERT_NO_FATAL_FAILURE(TransferTest(fd, pair2->first_fd())); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_P(UnixStreamSocketPairTest, CredPassPartialRead) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data[20]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - - struct ucred sent_creds; - - ASSERT_THAT(sent_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(sent_creds.gid = getgid(), SyscallSucceeds()); - - ASSERT_NO_FATAL_FAILURE( - SendCreds(sockets->first_fd(), sent_creds, sent_data, sizeof(sent_data))); - - int one = 1; - ASSERT_THAT(setsockopt(sockets->second_fd(), SOL_SOCKET, SO_PASSCRED, &one, - sizeof(one)), - SyscallSucceeds()); - - for (int i = 0; i < 2; i++) { - char received_data[10]; - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data), - sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data + i * sizeof(received_data), received_data, - sizeof(received_data))); - EXPECT_EQ(sent_creds.pid, received_creds.pid); - EXPECT_EQ(sent_creds.uid, received_creds.uid); - EXPECT_EQ(sent_creds.gid, received_creds.gid); - } -} - -// Unix stream sockets peek in the same way as datagram sockets. -// -// SinglePeek checks that only a single message is peekable in a single recv. -TEST_P(UnixStreamSocketPairTest, SinglePeek) { - if (!IsRunningOnGvisor()) { - // Don't run this test on linux kernels newer than 4.3.x Linux kernel commit - // 9f389e35674f5b086edd70ed524ca0f287259725 which changes this behavior. We - // used to target 3.11 compatibility, so disable this test on newer kernels. - // - // NOTE(b/118902768): Bring this up to Linux 4.4 compatibility. - auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion()); - SKIP_IF(version.major > 4 || (version.major == 4 && version.minor >= 3)); - } - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - char sent_data[40]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(RetryEINTR(send)(sockets->first_fd(), sent_data, - sizeof(sent_data) / 2, 0), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - ASSERT_THAT( - RetryEINTR(send)(sockets->first_fd(), sent_data + sizeof(sent_data) / 2, - sizeof(sent_data) / 2, 0), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - char received_data[sizeof(sent_data)]; - for (int i = 0; i < 3; i++) { - memset(received_data, 0, sizeof(received_data)); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(received_data), MSG_PEEK), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); - } - memset(received_data, 0, sizeof(received_data)); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(sent_data) / 2, 0), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); - memset(received_data, 0, sizeof(received_data)); - ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), received_data, - sizeof(sent_data) / 2, 0), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - EXPECT_EQ(0, memcmp(sent_data + sizeof(sent_data) / 2, received_data, - sizeof(sent_data) / 2)); -} - -TEST_P(UnixStreamSocketPairTest, CredsNotCoalescedUp) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - SetSoPassCred(sockets->second_fd()); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - - struct ucred received_creds; - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data), - sizeof(sent_data1))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - - struct ucred want_creds { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data), - sizeof(sent_data2))); - - EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(sent_data2))); - - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixStreamSocketPairTest, CredsNotCoalescedDown) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->second_fd()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - UnsetSoPassCred(sockets->second_fd()); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data), - sizeof(sent_data1))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data), - sizeof(sent_data2))); - - EXPECT_EQ(0, memcmp(sent_data2, received_data, sizeof(sent_data2))); - - want_creds = {0, 65534, 65534}; - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixStreamSocketPairTest, CoalescedCredsNoPasscred) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->second_fd()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - UnsetSoPassCred(sockets->second_fd()); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), - sizeof(sent_data2))); -} - -TEST_P(UnixStreamSocketPairTest, CoalescedCreds1) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - SetSoPassCred(sockets->second_fd()); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), - sizeof(sent_data2))); - - struct ucred want_creds { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixStreamSocketPairTest, CoalescedCreds2) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->second_fd()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds, - received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), - sizeof(sent_data2))); - - struct ucred want_creds; - ASSERT_THAT(want_creds.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds.pid, received_creds.pid); - EXPECT_EQ(want_creds.uid, received_creds.uid); - EXPECT_EQ(want_creds.gid, received_creds.gid); -} - -TEST_P(UnixStreamSocketPairTest, NonCoalescedDifferingCreds1) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - SetSoPassCred(sockets->second_fd()); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - char received_data1[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds1; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds1, - received_data1, sizeof(sent_data1))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1))); - - struct ucred want_creds1 { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds1.pid, received_creds1.pid); - EXPECT_EQ(want_creds1.uid, received_creds1.uid); - EXPECT_EQ(want_creds1.gid, received_creds1.gid); - - char received_data2[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds2; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds2, - received_data2, sizeof(sent_data2))); - - EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2))); - - struct ucred want_creds2; - ASSERT_THAT(want_creds2.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds2.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds2.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds2.pid, received_creds2.pid); - EXPECT_EQ(want_creds2.uid, received_creds2.uid); - EXPECT_EQ(want_creds2.gid, received_creds2.gid); -} - -TEST_P(UnixStreamSocketPairTest, NonCoalescedDifferingCreds2) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->second_fd()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - UnsetSoPassCred(sockets->second_fd()); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - SetSoPassCred(sockets->second_fd()); - - char received_data1[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds1; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds1, - received_data1, sizeof(sent_data1))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data1, sizeof(sent_data1))); - - struct ucred want_creds1; - ASSERT_THAT(want_creds1.pid = getpid(), SyscallSucceeds()); - ASSERT_THAT(want_creds1.uid = getuid(), SyscallSucceeds()); - ASSERT_THAT(want_creds1.gid = getgid(), SyscallSucceeds()); - - EXPECT_EQ(want_creds1.pid, received_creds1.pid); - EXPECT_EQ(want_creds1.uid, received_creds1.uid); - EXPECT_EQ(want_creds1.gid, received_creds1.gid); - - char received_data2[sizeof(sent_data1) + sizeof(sent_data2)]; - struct ucred received_creds2; - - ASSERT_NO_FATAL_FAILURE(RecvCreds(sockets->second_fd(), &received_creds2, - received_data2, sizeof(sent_data2))); - - EXPECT_EQ(0, memcmp(sent_data2, received_data2, sizeof(sent_data2))); - - struct ucred want_creds2 { - 0, 65534, 65534 - }; - - EXPECT_EQ(want_creds2.pid, received_creds2.pid); - EXPECT_EQ(want_creds2.uid, received_creds2.uid); - EXPECT_EQ(want_creds2.gid, received_creds2.gid); -} - -TEST_P(UnixStreamSocketPairTest, CoalescedDifferingCreds) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - SetSoPassCred(sockets->second_fd()); - - char sent_data1[20]; - RandomizeBuffer(sent_data1, sizeof(sent_data1)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data1, sizeof(sent_data1)), - SyscallSucceedsWithValue(sizeof(sent_data1))); - - char sent_data2[20]; - RandomizeBuffer(sent_data2, sizeof(sent_data2)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data2, sizeof(sent_data2)), - SyscallSucceedsWithValue(sizeof(sent_data2))); - - UnsetSoPassCred(sockets->second_fd()); - - char sent_data3[20]; - RandomizeBuffer(sent_data3, sizeof(sent_data3)); - - ASSERT_THAT(WriteFd(sockets->first_fd(), sent_data3, sizeof(sent_data3)), - SyscallSucceedsWithValue(sizeof(sent_data3))); - - char received_data[sizeof(sent_data1) + sizeof(sent_data2) + - sizeof(sent_data3)]; - - ASSERT_NO_FATAL_FAILURE( - RecvNoCmsg(sockets->second_fd(), received_data, sizeof(received_data))); - - EXPECT_EQ(0, memcmp(sent_data1, received_data, sizeof(sent_data1))); - EXPECT_EQ(0, memcmp(sent_data2, received_data + sizeof(sent_data1), - sizeof(sent_data2))); - EXPECT_EQ(0, memcmp(sent_data3, - received_data + sizeof(sent_data1) + sizeof(sent_data2), - sizeof(sent_data3))); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnixStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>(UnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>(FilesystemBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractBoundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK})))))); - -// Test fixture for tests that apply to pairs of unbound unix stream sockets. -using UnboundUnixStreamSocketPairTest = SocketPairTest; - -TEST_P(UnboundUnixStreamSocketPairTest, SendtoWithoutConnect) { - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - char data = 'a'; - ASSERT_THAT(sendto(sockets->second_fd(), &data, sizeof(data), 0, - sockets->first_addr(), sockets->first_addr_size()), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -TEST_P(UnboundUnixStreamSocketPairTest, SendtoWithoutConnectIgnoresAddr) { - // FIXME(b/68223466): gVisor tries to find /foo/bar and thus returns ENOENT. - if (IsRunningOnGvisor()) { - return; - } - - auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); - - ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(), - sockets->first_addr_size()), - SyscallSucceeds()); - - // Even a bogus address is completely ignored. - constexpr char kPath[] = "/foo/bar"; - - // Sanity check that kPath doesn't exist. - struct stat s; - ASSERT_THAT(stat(kPath, &s), SyscallFailsWithErrno(ENOENT)); - - struct sockaddr_un addr = {}; - addr.sun_family = AF_UNIX; - memcpy(addr.sun_path, kPath, sizeof(kPath)); - - char data = 'a'; - ASSERT_THAT( - sendto(sockets->second_fd(), &data, sizeof(data), 0, - reinterpret_cast<const struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -INSTANTIATE_TEST_SUITE_P( - AllUnixDomainSockets, UnboundUnixStreamSocketPairTest, - ::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>( - ApplyVec<SocketPairKind>(FilesystemUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{ - 0, SOCK_NONBLOCK})), - ApplyVec<SocketPairKind>( - AbstractUnboundUnixDomainSocketPair, - AllBitwiseCombinations(List<int>{SOCK_STREAM}, - List<int>{0, SOCK_NONBLOCK})))))); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc deleted file mode 100644 index e5730a606..000000000 --- a/test/syscalls/linux/splice.cc +++ /dev/null @@ -1,939 +0,0 @@ -// Copyright 2019 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 <fcntl.h> -#include <linux/unistd.h> -#include <sys/eventfd.h> -#include <sys/resource.h> -#include <sys/sendfile.h> -#include <sys/time.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/file_descriptor.h" -#include "test/util/signal_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SpliceTest, TwoRegularFiles) { - // Create temp files. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Open the input file as read only. - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Open the output file as write only. - const FileDescriptor out_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_WRONLY)); - - // Verify that it is rejected as expected; regardless of offsets. - loff_t in_offset = 0; - loff_t out_offset = 0; - EXPECT_THAT(splice(in_fd.get(), &in_offset, out_fd.get(), &out_offset, 1, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(splice(in_fd.get(), nullptr, out_fd.get(), &out_offset, 1, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(splice(in_fd.get(), &in_offset, out_fd.get(), nullptr, 1, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(splice(in_fd.get(), nullptr, out_fd.get(), nullptr, 1, 0), - 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]; - 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)); - - // Attempt to splice to itself. - EXPECT_THAT(splice(rfd.get(), nullptr, wfd.get(), nullptr, kPageSize, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(TeeTest, SamePipe) { - // 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)); - - // Attempt to tee to itself. - EXPECT_THAT(tee(rfd.get(), wfd.get(), kPageSize, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(TeeTest, RegularFile) { - // Open some file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Attempt to tee from the file. - EXPECT_THAT(tee(in_fd.get(), wfd.get(), kPageSize, 0), - SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(tee(rfd.get(), in_fd.get(), kPageSize, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SpliceTest, PipeOffsets) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // All pipe offsets should be rejected. - loff_t in_offset = 0; - loff_t out_offset = 0; - EXPECT_THAT(splice(rfd1.get(), &in_offset, wfd2.get(), &out_offset, 1, 0), - SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(splice(rfd1.get(), nullptr, wfd2.get(), &out_offset, 1, 0), - SyscallFailsWithErrno(ESPIPE)); - EXPECT_THAT(splice(rfd1.get(), &in_offset, wfd2.get(), nullptr, 1, 0), - SyscallFailsWithErrno(ESPIPE)); -} - -// Event FDs may be used with splice without an offset. -TEST(SpliceTest, FromEventFD) { - // Open the input eventfd with an initial value so that it is readable. - constexpr uint64_t kEventFDValue = 1; - int efd; - ASSERT_THAT(efd = eventfd(kEventFDValue, 0), SyscallSucceeds()); - const FileDescriptor in_fd(efd); - - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Splice 8-byte eventfd value to pipe. - constexpr int kEventFDSize = 8; - EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, kEventFDSize, 0), - SyscallSucceedsWithValue(kEventFDSize)); - - // Contents should be equal. - std::vector<char> rbuf(kEventFDSize); - ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kEventFDSize)); - EXPECT_EQ(memcmp(rbuf.data(), &kEventFDValue, rbuf.size()), 0); -} - -// Event FDs may not be used with splice with an offset. -TEST(SpliceTest, FromEventFDOffset) { - int efd; - ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds()); - const FileDescriptor in_fd(efd); - - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Attempt to splice 8-byte eventfd value to pipe with offset. - // - // This is not allowed because eventfd doesn't support pread. - constexpr int kEventFDSize = 8; - loff_t in_off = 0; - EXPECT_THAT(splice(in_fd.get(), &in_off, wfd.get(), nullptr, kEventFDSize, 0), - SyscallFailsWithErrno(EINVAL)); -} - -// Event FDs may not be used with splice with an offset. -TEST(SpliceTest, ToEventFDOffset) { - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Fill with a value. - constexpr int kEventFDSize = 8; - std::vector<char> buf(kEventFDSize); - buf[0] = 1; - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kEventFDSize)); - - int efd; - ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds()); - const FileDescriptor out_fd(efd); - - // Attempt to splice 8-byte eventfd value to pipe with offset. - // - // This is not allowed because eventfd doesn't support pwrite. - loff_t out_off = 0; - EXPECT_THAT( - splice(rfd.get(), nullptr, out_fd.get(), &out_off, kEventFDSize, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SpliceTest, ToPipe) { - // Open the input file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - - // Fill with some random data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(in_fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - ASSERT_THAT(lseek(in_fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Splice to the pipe. - EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // Contents should be equal. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); -} - -TEST(SpliceTest, ToPipeEOF) { - // Create and open an empty input file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY)); - - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Splice from the empty file to the pipe. - EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, 123, 0), - SyscallSucceedsWithValue(0)); -} - -TEST(SpliceTest, ToPipeOffset) { - // Open the input file. - const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor in_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR)); - - // Fill with some random data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(in_fd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Splice to the pipe. - loff_t in_offset = kPageSize / 2; - EXPECT_THAT( - splice(in_fd.get(), &in_offset, wfd.get(), nullptr, kPageSize / 2, 0), - SyscallSucceedsWithValue(kPageSize / 2)); - - // Contents should be equal to only the second part. - std::vector<char> rbuf(kPageSize / 2); - ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize / 2)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data() + (kPageSize / 2), rbuf.size()), 0); -} - -TEST(SpliceTest, FromPipe) { - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Fill with some random data. - 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. - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor out_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR)); - - // Splice to the output file. - EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // The offset of the output should be equal to kPageSize. We assert that and - // reset to zero so that we can read the contents and ensure they match. - EXPECT_THAT(lseek(out_fd.get(), 0, SEEK_CUR), - SyscallSucceedsWithValue(kPageSize)); - ASSERT_THAT(lseek(out_fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Contents should be equal. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(out_fd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); -} - -TEST(SpliceTest, FromPipeMultiple) { - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - std::string buf = "abcABC123"; - ASSERT_THAT(write(wfd.get(), buf.c_str(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Open the output file. - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor out_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR)); - - // Splice from the pipe to the output file over several calls. - EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3, 0), - SyscallSucceedsWithValue(3)); - EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3, 0), - SyscallSucceedsWithValue(3)); - EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3, 0), - SyscallSucceedsWithValue(3)); - - // Reset cursor to zero so that we can check the contents. - ASSERT_THAT(lseek(out_fd.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0)); - - // Contents should be equal. - std::vector<char> rbuf(buf.size()); - ASSERT_THAT(read(out_fd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(rbuf.size())); - EXPECT_EQ(memcmp(rbuf.data(), buf.c_str(), buf.size()), 0); -} - -TEST(SpliceTest, FromPipeOffset) { - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Fill with some random data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Open the input file. - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor out_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR)); - - // Splice to the output file. - loff_t out_offset = kPageSize / 2; - EXPECT_THAT( - splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // Content should reflect the splice. We write to a specific offset in the - // file, so the internals should now be allocated sparsely. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(out_fd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - std::vector<char> zbuf(kPageSize / 2); - memset(zbuf.data(), 0, zbuf.size()); - EXPECT_EQ(memcmp(rbuf.data(), zbuf.data(), zbuf.size()), 0); - EXPECT_EQ(memcmp(rbuf.data() + kPageSize / 2, buf.data(), kPageSize / 2), 0); -} - -TEST(SpliceTest, TwoPipes) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // Fill with some random data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(wfd1.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Splice to the second pipe, using two operations. - EXPECT_THAT( - splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize / 2, 0), - SyscallSucceedsWithValue(kPageSize / 2)); - EXPECT_THAT( - splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize / 2, 0), - SyscallSucceedsWithValue(kPageSize / 2)); - - // Content should reflect the splice. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0); -} - -TEST(SpliceTest, TwoPipesPartialRead) { - // Create two pipes. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor first_rfd(fds[0]); - const FileDescriptor first_wfd(fds[1]); - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor second_rfd(fds[0]); - const FileDescriptor second_wfd(fds[1]); - - // Write half a page of data to the first pipe. - std::vector<char> buf(kPageSize / 2); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(first_wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize / 2)); - - // Attempt to splice one page from the first pipe to the second; it should - // immediately return after splicing the half-page previously written to the - // first pipe. - EXPECT_THAT( - splice(first_rfd.get(), nullptr, second_wfd.get(), nullptr, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize / 2)); -} - -TEST(SpliceTest, TwoPipesPartialWrite) { - // Create two pipes. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor first_rfd(fds[0]); - const FileDescriptor first_wfd(fds[1]); - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor second_rfd(fds[0]); - const FileDescriptor second_wfd(fds[1]); - - // Write two pages of data to the first pipe. - std::vector<char> buf(2 * kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(first_wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(2 * kPageSize)); - - // Limit the second pipe to two pages, then write one page of data to it. - ASSERT_THAT(fcntl(second_wfd.get(), F_SETPIPE_SZ, 2 * kPageSize), - SyscallSucceeds()); - ASSERT_THAT(write(second_wfd.get(), buf.data(), buf.size() / 2), - SyscallSucceedsWithValue(kPageSize)); - - // Attempt to splice two pages from the first pipe to the second; it should - // immediately return after splicing the first page previously written to the - // first pipe. - EXPECT_THAT(splice(first_rfd.get(), nullptr, second_wfd.get(), nullptr, - 2 * kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); -} - -TEST(TeeTest, TwoPipesPartialRead) { - // Create two pipes. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor first_rfd(fds[0]); - const FileDescriptor first_wfd(fds[1]); - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor second_rfd(fds[0]); - const FileDescriptor second_wfd(fds[1]); - - // Write half a page of data to the first pipe. - std::vector<char> buf(kPageSize / 2); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(first_wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize / 2)); - - // Attempt to tee one page from the first pipe to the second; it should - // immediately return after copying the half-page previously written to the - // first pipe. - EXPECT_THAT(tee(first_rfd.get(), second_wfd.get(), kPageSize, 0), - SyscallSucceedsWithValue(kPageSize / 2)); -} - -TEST(TeeTest, TwoPipesPartialWrite) { - // Create two pipes. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor first_rfd(fds[0]); - const FileDescriptor first_wfd(fds[1]); - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor second_rfd(fds[0]); - const FileDescriptor second_wfd(fds[1]); - - // Write two pages of data to the first pipe. - std::vector<char> buf(2 * kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(first_wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(2 * kPageSize)); - - // Limit the second pipe to two pages, then write one page of data to it. - ASSERT_THAT(fcntl(second_wfd.get(), F_SETPIPE_SZ, 2 * kPageSize), - SyscallSucceeds()); - ASSERT_THAT(write(second_wfd.get(), buf.data(), buf.size() / 2), - SyscallSucceedsWithValue(kPageSize)); - - // Attempt to tee two pages from the first pipe to the second; it should - // immediately return after copying the first page previously written to the - // first pipe. - EXPECT_THAT(tee(first_rfd.get(), second_wfd.get(), 2 * kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); -} - -TEST(SpliceTest, TwoPipesCircular) { - // This test deadlocks the sentry on VFS1 because VFS1 splice ordering is - // based on fs.File.UniqueID, which does not prevent circular ordering between - // e.g. inode-level locks taken by fs.FileOperations. - SKIP_IF(IsRunningWithVFS1()); - - // Create two pipes. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor first_rfd(fds[0]); - const FileDescriptor first_wfd(fds[1]); - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor second_rfd(fds[0]); - const FileDescriptor second_wfd(fds[1]); - - // On Linux, each pipe is normally limited to - // include/linux/pipe_fs_i.h:PIPE_DEF_BUFFERS buffers worth of data. - constexpr size_t PIPE_DEF_BUFFERS = 16; - - // Write some data to each pipe. Below we splice 1 byte at a time between - // pipes, which very quickly causes each byte to be stored in a separate - // buffer, so we must ensure that the total amount of data in the system is <= - // PIPE_DEF_BUFFERS bytes. - std::vector<char> buf(PIPE_DEF_BUFFERS / 2); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(first_wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - ASSERT_THAT(write(second_wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Have another thread splice from the second pipe to the first, while we - // splice from the first to the second. The test passes if this does not - // deadlock. - const int kIterations = 1000; - DisableSave ds; - ScopedThread t([&]() { - for (int i = 0; i < kIterations; i++) { - ASSERT_THAT( - splice(second_rfd.get(), nullptr, first_wfd.get(), nullptr, 1, 0), - SyscallSucceedsWithValue(1)); - } - }); - for (int i = 0; i < kIterations; i++) { - ASSERT_THAT( - splice(first_rfd.get(), nullptr, second_wfd.get(), nullptr, 1, 0), - SyscallSucceedsWithValue(1)); - } -} - -TEST(SpliceTest, Blocking) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // This thread writes to the main pipe. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ScopedThread t([&]() { - ASSERT_THAT(write(wfd1.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - }); - - // Attempt a splice immediately; it should block. - EXPECT_THAT(splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // Thread should be joinable. - t.Join(); - - // Content should reflect the splice. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0); -} - -TEST(TeeTest, Blocking) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // This thread writes to the main pipe. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ScopedThread t([&]() { - ASSERT_THAT(write(wfd1.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - }); - - // Attempt a tee immediately; it should block. - EXPECT_THAT(tee(rfd1.get(), wfd2.get(), kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // Thread should be joinable. - t.Join(); - - // Content should reflect the splice, in both pipes. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0); - ASSERT_THAT(read(rfd1.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), kPageSize), 0); -} - -TEST(TeeTest, BlockingWrite) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // Make some data available to be read. - std::vector<char> buf1(kPageSize); - RandomizeBuffer(buf1.data(), buf1.size()); - ASSERT_THAT(write(wfd1.get(), buf1.data(), buf1.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Fill up the write pipe's buffer. - int pipe_size = -1; - ASSERT_THAT(pipe_size = fcntl(wfd2.get(), F_GETPIPE_SZ), SyscallSucceeds()); - std::vector<char> buf2(pipe_size); - ASSERT_THAT(write(wfd2.get(), buf2.data(), buf2.size()), - SyscallSucceedsWithValue(pipe_size)); - - ScopedThread t([&]() { - absl::SleepFor(absl::Milliseconds(100)); - ASSERT_THAT(read(rfd2.get(), buf2.data(), buf2.size()), - SyscallSucceedsWithValue(pipe_size)); - }); - - // Attempt a tee immediately; it should block. - EXPECT_THAT(tee(rfd1.get(), wfd2.get(), kPageSize, 0), - SyscallSucceedsWithValue(kPageSize)); - - // Thread should be joinable. - t.Join(); - - // Content should reflect the tee. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf1.data(), kPageSize), 0); -} - -TEST(SpliceTest, NonBlocking) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // Splice with no data to back it. - EXPECT_THAT(splice(rfd1.get(), nullptr, wfd2.get(), nullptr, kPageSize, - SPLICE_F_NONBLOCK), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(TeeTest, NonBlocking) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // Splice with no data to back it. - EXPECT_THAT(tee(rfd1.get(), wfd2.get(), kPageSize, SPLICE_F_NONBLOCK), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(TeeTest, MultiPage) { - // Create two new pipes. - int first[2], second[2]; - ASSERT_THAT(pipe(first), SyscallSucceeds()); - const FileDescriptor rfd1(first[0]); - const FileDescriptor wfd1(first[1]); - ASSERT_THAT(pipe(second), SyscallSucceeds()); - const FileDescriptor rfd2(second[0]); - const FileDescriptor wfd2(second[1]); - - // Make some data available to be read. - std::vector<char> wbuf(8 * kPageSize); - RandomizeBuffer(wbuf.data(), wbuf.size()); - ASSERT_THAT(write(wfd1.get(), wbuf.data(), wbuf.size()), - SyscallSucceedsWithValue(wbuf.size())); - - // Attempt a tee immediately; it should complete. - EXPECT_THAT(tee(rfd1.get(), wfd2.get(), wbuf.size(), 0), - SyscallSucceedsWithValue(wbuf.size())); - - // Content should reflect the tee. - std::vector<char> rbuf(wbuf.size()); - ASSERT_THAT(read(rfd2.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(rbuf.size())); - EXPECT_EQ(memcmp(rbuf.data(), wbuf.data(), rbuf.size()), 0); - ASSERT_THAT(read(rfd1.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(rbuf.size())); - EXPECT_EQ(memcmp(rbuf.data(), wbuf.data(), rbuf.size()), 0); -} - -TEST(SpliceTest, FromPipeMaxFileSize) { - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - const FileDescriptor wfd(fds[1]); - - // Fill with some random data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - // Open the input file. - const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor out_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(out_file.path(), O_RDWR)); - - EXPECT_THAT(ftruncate(out_fd.get(), 13 << 20), SyscallSucceeds()); - EXPECT_THAT(lseek(out_fd.get(), 0, SEEK_END), - SyscallSucceedsWithValue(13 << 20)); - - // Set our file size limit. - sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGXFSZ); - TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0); - rlimit rlim = {}; - rlim.rlim_cur = rlim.rlim_max = (13 << 20); - EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &rlim), SyscallSucceeds()); - - // Splice to the output file. - EXPECT_THAT( - splice(rfd.get(), nullptr, out_fd.get(), nullptr, 3 * kPageSize, 0), - SyscallFailsWithErrno(EFBIG)); - - // Contents should be equal. - std::vector<char> rbuf(kPageSize); - ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); -} - -TEST(SpliceTest, FromPipeToDevZero) { - // Create a new pipe. - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - const FileDescriptor rfd(fds[0]); - FileDescriptor wfd(fds[1]); - - // Fill with some random data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(kPageSize)); - - const FileDescriptor zero = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY)); - - // Close the write end to prevent blocking below. - wfd.reset(); - - // Splice to /dev/zero. The first call should empty the pipe, and the return - // value should not exceed the number of bytes available for reading. - EXPECT_THAT( - splice(rfd.get(), nullptr, zero.get(), nullptr, kPageSize + 123, 0), - SyscallSucceedsWithValue(kPageSize)); - EXPECT_THAT(splice(rfd.get(), nullptr, zero.get(), nullptr, 1, 0), - SyscallSucceedsWithValue(0)); -} - -static volatile int signaled = 0; -void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; } - -TEST(SpliceTest, ToPipeWithSmallCapacityDoesNotSpin_NoRandomSave) { - // Writes to a pipe that are less than PIPE_BUF must be atomic. This test - // creates a pipe with only 128 bytes of capacity (< PIPE_BUF) and checks that - // splicing to the pipe does not spin. See b/170743336. - - // Create a file with one page of data. - std::vector<char> buf(kPageSize); - RandomizeBuffer(buf.data(), buf.size()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::string_view(buf.data(), buf.size()), - TempPath::kDefaultFileMode)); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - // Create a pipe with size 4096, and fill all but 128 bytes of it. - int p[2]; - ASSERT_THAT(pipe(p), SyscallSucceeds()); - ASSERT_THAT(fcntl(p[1], F_SETPIPE_SZ, kPageSize), SyscallSucceeds()); - const int kWriteSize = kPageSize - 128; - std::vector<char> writeBuf(kWriteSize); - RandomizeBuffer(writeBuf.data(), writeBuf.size()); - ASSERT_THAT(write(p[1], writeBuf.data(), writeBuf.size()), - SyscallSucceedsWithValue(kWriteSize)); - - // Set up signal handler. - struct sigaction sa = {}; - sa.sa_sigaction = SigUsr1Handler; - sa.sa_flags = SA_SIGINFO; - const auto cleanup_sigact = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa)); - - // Send SIGUSR1 to this thread in 1 second. - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = SIGUSR1; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - struct itimerspec its = {}; - its.it_value = absl::ToTimespec(absl::Seconds(1)); - DisableSave ds; // Asserting an EINTR. - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // Now splice the file to the pipe. This should block, but not spin, and - // should return EINTR because it is interrupted by the signal. - EXPECT_THAT(splice(fd.get(), nullptr, p[1], nullptr, kPageSize, 0), - SyscallFailsWithErrno(EINTR)); - - // Alarm should have been handled. - EXPECT_EQ(signaled, 1); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc deleted file mode 100644 index 72f888659..000000000 --- a/test/syscalls/linux/stat.cc +++ /dev/null @@ -1,799 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/statfs.h> -#include <sys/types.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/match.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/string_view.h" -#include "test/syscalls/linux/file_base.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/save_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -#ifndef AT_STATX_FORCE_SYNC -#define AT_STATX_FORCE_SYNC 0x2000 -#endif -#ifndef AT_STATX_DONT_SYNC -#define AT_STATX_DONT_SYNC 0x4000 -#endif - -namespace gvisor { -namespace testing { - -namespace { - -class StatTest : public FileTest {}; - -TEST_F(StatTest, FstatatAbs) { - struct stat st; - - // Check that the stat works. - EXPECT_THAT(fstatat(AT_FDCWD, test_file_name_.c_str(), &st, 0), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(st.st_mode)); -} - -TEST_F(StatTest, FstatatEmptyPath) { - struct stat st; - const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - - // Check that the stat works. - EXPECT_THAT(fstatat(fd.get(), "", &st, AT_EMPTY_PATH), SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(st.st_mode)); -} - -TEST_F(StatTest, FstatatRel) { - struct stat st; - int dirfd; - auto filename = std::string(Basename(test_file_name_)); - - // Open the temporary directory read-only. - ASSERT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY), - SyscallSucceeds()); - - // Check that the stat works. - EXPECT_THAT(fstatat(dirfd, filename.c_str(), &st, 0), SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(st.st_mode)); - close(dirfd); -} - -TEST_F(StatTest, FstatatSymlink) { - struct stat st; - - // Check that the link is followed. - EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, 0), SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - EXPECT_FALSE(S_ISLNK(st.st_mode)); - - // Check that the flag works. - EXPECT_THAT(fstatat(AT_FDCWD, "/proc/self", &st, AT_SYMLINK_NOFOLLOW), - SyscallSucceeds()); - EXPECT_TRUE(S_ISLNK(st.st_mode)); - EXPECT_FALSE(S_ISDIR(st.st_mode)); -} - -TEST_F(StatTest, Nlinks) { - // Skip this test if we are testing overlayfs because overlayfs does not - // (intentionally) return the correct nlink value for directories. - // See fs/overlayfs/inode.c:ovl_getattr(). - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))); - - TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Directory is initially empty, it should contain 2 links (one from itself, - // one from "."). - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); - - // Create a file in the test directory. Files shouldn't increase the link - // count on the base directory. - TempPath file1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); - - // Create subdirectories. This should increase the link count by 1 per - // subdirectory. - TempPath dir1 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3)); - TempPath dir2 = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(4)); - - // Removing directories should reduce the link count. - dir1.reset(); - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(3)); - dir2.reset(); - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); - - // Removing files should have no effect on link count. - file1.reset(); - EXPECT_THAT(Links(basedir.path()), IsPosixErrorOkAndHolds(2)); -} - -TEST_F(StatTest, BlocksIncreaseOnWrite) { - struct stat st; - - // Stat the empty file. - ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds()); - - const int initial_blocks = st.st_blocks; - - // Write to the file, making sure to exceed the block size. - std::vector<char> buf(2 * st.st_blksize, 'a'); - ASSERT_THAT(write(test_file_fd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Stat the file again, and verify that number of allocated blocks has - // increased. - ASSERT_THAT(fstat(test_file_fd_.get(), &st), SyscallSucceeds()); - EXPECT_GT(st.st_blocks, initial_blocks); -} - -TEST_F(StatTest, PathNotCleaned) { - TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Create a file in the basedir. - TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); - - // Stating the file directly should succeed. - struct stat buf; - EXPECT_THAT(lstat(file.path().c_str(), &buf), SyscallSucceeds()); - - // Try to stat the file using a directory that does not exist followed by - // "..". If the path is cleaned prior to stating (which it should not be) - // then this will succeed. - const std::string bad_path = JoinPath("/does_not_exist/..", file.path()); - EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOENT)); -} - -TEST_F(StatTest, PathCanContainDotDot) { - TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath subdir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(basedir.path())); - const std::string subdir_name = std::string(Basename(subdir.path())); - - // Create a file in the subdir. - TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(subdir.path())); - const std::string file_name = std::string(Basename(file.path())); - - // Stat the file through a path that includes '..' and '.' but still resolves - // to the file. - const std::string good_path = - JoinPath(basedir.path(), subdir_name, "..", subdir_name, ".", file_name); - struct stat buf; - EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds()); -} - -TEST_F(StatTest, PathCanContainEmptyComponent) { - TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Create a file in the basedir. - TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); - const std::string file_name = std::string(Basename(file.path())); - - // Stat the file through a path that includes an empty component. We have to - // build this ourselves because JoinPath automatically removes empty - // components. - const std::string good_path = absl::StrCat(basedir.path(), "//", file_name); - struct stat buf; - EXPECT_THAT(lstat(good_path.c_str(), &buf), SyscallSucceeds()); -} - -TEST_F(StatTest, TrailingSlashNotCleanedReturnsENOTDIR) { - TempPath basedir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Create a file in the basedir. - TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(basedir.path())); - - // Stat the file with an extra "/" on the end of it. Since file is not a - // directory, this should return ENOTDIR. - const std::string bad_path = absl::StrCat(file.path(), "/"); - struct stat buf; - EXPECT_THAT(lstat(bad_path.c_str(), &buf), SyscallFailsWithErrno(ENOTDIR)); -} - -TEST_F(StatTest, FstatFileWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - struct stat st; - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); - - // Stat the directory. - ASSERT_THAT(fstat(fd.get(), &st), SyscallSucceeds()); -} - -TEST_F(StatTest, FstatDirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - struct stat st; - TempPath tmpdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( - Open(tmpdir.path().c_str(), O_PATH | O_DIRECTORY)); - - // Stat the directory. - ASSERT_THAT(fstat(dirfd.get(), &st), SyscallSucceeds()); -} - -// fstatat with an O_PATH fd -TEST_F(StatTest, FstatatDirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath tmpdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( - Open(tmpdir.path().c_str(), O_PATH | O_DIRECTORY)); - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - struct stat st = {}; - EXPECT_THAT(fstatat(dirfd.get(), tmpfile.path().c_str(), &st, 0), - SyscallSucceeds()); - EXPECT_FALSE(S_ISDIR(st.st_mode)); - EXPECT_TRUE(S_ISREG(st.st_mode)); -} - -// Test fstatating a symlink directory. -TEST_F(StatTest, FstatatSymlinkDir) { - // Create a directory and symlink to it. - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - const std::string symlink_to_dir = NewTempAbsPath(); - EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), - SyscallSucceeds()); - auto cleanup = Cleanup([&symlink_to_dir]() { - EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); - }); - - // Fstatat the link with AT_SYMLINK_NOFOLLOW should return symlink data. - struct stat st = {}; - EXPECT_THAT( - fstatat(AT_FDCWD, symlink_to_dir.c_str(), &st, AT_SYMLINK_NOFOLLOW), - SyscallSucceeds()); - EXPECT_FALSE(S_ISDIR(st.st_mode)); - EXPECT_TRUE(S_ISLNK(st.st_mode)); - - // Fstatat the link should return dir data. - EXPECT_THAT(fstatat(AT_FDCWD, symlink_to_dir.c_str(), &st, 0), - SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - EXPECT_FALSE(S_ISLNK(st.st_mode)); -} - -// Test fstatating a symlink directory with trailing slash. -TEST_F(StatTest, FstatatSymlinkDirWithTrailingSlash) { - // Create a directory and symlink to it. - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string symlink_to_dir = NewTempAbsPath(); - EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), - SyscallSucceeds()); - auto cleanup = Cleanup([&symlink_to_dir]() { - EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); - }); - - // Fstatat on the symlink with a trailing slash should return the directory - // data. - struct stat st = {}; - EXPECT_THAT( - fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, 0), - SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - EXPECT_FALSE(S_ISLNK(st.st_mode)); - - // Fstatat on the symlink with a trailing slash with AT_SYMLINK_NOFOLLOW - // should return the directory data. - // Symlink to directory with trailing slash will ignore AT_SYMLINK_NOFOLLOW. - EXPECT_THAT(fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, - AT_SYMLINK_NOFOLLOW), - SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - EXPECT_FALSE(S_ISLNK(st.st_mode)); -} - -// Test fstatating a symlink directory with a trailing slash -// should return same stat data with fstatating directory. -TEST_F(StatTest, FstatatSymlinkDirWithTrailingSlashSameInode) { - // Create a directory and symlink to it. - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // We are going to assert that the symlink inode id is the same as the linked - // dir's inode id. In order for the inode id to be stable across - // save/restore, it must be kept open. The FileDescriptor type will do that - // for us automatically. - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); - - const std::string symlink_to_dir = NewTempAbsPath(); - EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), - SyscallSucceeds()); - auto cleanup = Cleanup([&symlink_to_dir]() { - EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); - }); - - // Fstatat on the symlink with a trailing slash should return the directory - // data. - struct stat st = {}; - EXPECT_THAT(fstatat(AT_FDCWD, absl::StrCat(symlink_to_dir, "/").c_str(), &st, - AT_SYMLINK_NOFOLLOW), - SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - - // Dir and symlink should point to same inode. - struct stat st_dir = {}; - EXPECT_THAT( - fstatat(AT_FDCWD, dir.path().c_str(), &st_dir, AT_SYMLINK_NOFOLLOW), - SyscallSucceeds()); - EXPECT_EQ(st.st_ino, st_dir.st_ino); -} - -TEST_F(StatTest, LeadingDoubleSlash) { - // Create a file, and make sure we can stat it. - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - struct stat st; - ASSERT_THAT(lstat(file.path().c_str(), &st), SyscallSucceeds()); - - // Now add an extra leading slash. - const std::string double_slash_path = absl::StrCat("/", file.path()); - ASSERT_TRUE(absl::StartsWith(double_slash_path, "//")); - - // We should be able to stat the new path, and it should resolve to the same - // file (same device and inode). - struct stat double_slash_st; - ASSERT_THAT(lstat(double_slash_path.c_str(), &double_slash_st), - SyscallSucceeds()); - EXPECT_EQ(st.st_dev, double_slash_st.st_dev); - // Inode numbers for gofer-accessed files may change across save/restore. - if (!IsRunningWithSaveRestore()) { - EXPECT_EQ(st.st_ino, double_slash_st.st_ino); - } -} - -// Test that a rename doesn't change the underlying file. -TEST_F(StatTest, StatDoesntChangeAfterRename) { - const TempPath old_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const TempPath new_path(NewTempAbsPath()); - - struct stat st_old = {}; - struct stat st_new = {}; - - ASSERT_THAT(stat(old_file.path().c_str(), &st_old), SyscallSucceeds()); - ASSERT_THAT(rename(old_file.path().c_str(), new_path.path().c_str()), - SyscallSucceeds()); - ASSERT_THAT(stat(new_path.path().c_str(), &st_new), SyscallSucceeds()); - - EXPECT_EQ(st_old.st_nlink, st_new.st_nlink); - EXPECT_EQ(st_old.st_dev, st_new.st_dev); - // Inode numbers for gofer-accessed files on which no reference is held may - // change across save/restore because the information that the gofer client - // uses to track file identity (9P QID path) is inconsistent between gofer - // processes, which are restarted across save/restore. - // - // Overlay filesystems may synthesize directory inode numbers on the fly. - if (!IsRunningWithSaveRestore() && - !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))) { - EXPECT_EQ(st_old.st_ino, st_new.st_ino); - } - EXPECT_EQ(st_old.st_mode, st_new.st_mode); - EXPECT_EQ(st_old.st_uid, st_new.st_uid); - EXPECT_EQ(st_old.st_gid, st_new.st_gid); - EXPECT_EQ(st_old.st_size, st_new.st_size); -} - -// Test link counts with a regular file as the child. -TEST_F(StatTest, LinkCountsWithRegularFileChild) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - struct stat st_parent_before = {}; - ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds()); - EXPECT_EQ(st_parent_before.st_nlink, 2); - - // Adding a regular file doesn't adjust the parent's link count. - const TempPath child = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - - struct stat st_parent_after = {}; - ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); - EXPECT_EQ(st_parent_after.st_nlink, 2); - - // The child should have a single link from the parent. - struct stat st_child = {}; - ASSERT_THAT(stat(child.path().c_str(), &st_child), SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(st_child.st_mode)); - EXPECT_EQ(st_child.st_nlink, 1); - - // Finally unlinking the child should not affect the parent's link count. - ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds()); - ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); - EXPECT_EQ(st_parent_after.st_nlink, 2); -} - -// This test verifies that inodes remain around when there is an open fd -// after link count hits 0. -// -// It is marked NoSave because we don't support saving unlinked files. -TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoSave) { - // Setting the enviornment variable GVISOR_GOFER_UNCACHED to any value - // will prevent this test from running, see the tmpfs lifecycle. - // - // We need to support this because when a file is unlinked and we forward - // the stat to the gofer it would return ENOENT. - const char* uncached_gofer = getenv("GVISOR_GOFER_UNCACHED"); - SKIP_IF(uncached_gofer != nullptr); - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - dir.path(), "hello", TempPath::kDefaultFileMode)); - - // The child should have a single link from the parent. - struct stat st_child_before = {}; - ASSERT_THAT(stat(child.path().c_str(), &st_child_before), SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(st_child_before.st_mode)); - EXPECT_EQ(st_child_before.st_nlink, 1); - EXPECT_EQ(st_child_before.st_size, 5); // Hello is 5 bytes. - - // Open the file so we can fstat after unlinking. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(child.path(), O_RDONLY)); - - // Now a stat should return ENOENT but we should still be able to stat - // via the open fd and fstat. - ASSERT_THAT(unlink(child.path().c_str()), SyscallSucceeds()); - - // Since the file has no more links stat should fail. - struct stat st_child_after = {}; - ASSERT_THAT(stat(child.path().c_str(), &st_child_after), - SyscallFailsWithErrno(ENOENT)); - - // Fstat should still allow us to access the same file via the fd. - struct stat st_child_fd = {}; - ASSERT_THAT(fstat(fd.get(), &st_child_fd), SyscallSucceeds()); - EXPECT_EQ(st_child_before.st_dev, st_child_fd.st_dev); - EXPECT_EQ(st_child_before.st_ino, st_child_fd.st_ino); - EXPECT_EQ(st_child_before.st_mode, st_child_fd.st_mode); - EXPECT_EQ(st_child_before.st_uid, st_child_fd.st_uid); - EXPECT_EQ(st_child_before.st_gid, st_child_fd.st_gid); - EXPECT_EQ(st_child_before.st_size, st_child_fd.st_size); - - // TODO(b/34861058): This isn't ideal but since fstatfs(2) will always return - // OVERLAYFS_SUPER_MAGIC we have no way to know if this fs is backed by a - // gofer which doesn't support links. - EXPECT_TRUE(st_child_fd.st_nlink == 0 || st_child_fd.st_nlink == 1); -} - -// Test link counts with a directory as the child. -TEST_F(StatTest, LinkCountsWithDirChild) { - // Skip this test if we are testing overlayfs because overlayfs does not - // (intentionally) return the correct nlink value for directories. - // See fs/overlayfs/inode.c:ovl_getattr(). - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))); - - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - // Before a child is added the two links are "." and the link from the parent. - struct stat st_parent_before = {}; - ASSERT_THAT(stat(dir.path().c_str(), &st_parent_before), SyscallSucceeds()); - EXPECT_EQ(st_parent_before.st_nlink, 2); - - // Create a subdirectory and stat for the parent link counts. - const TempPath sub_dir = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - - // The three links are ".", the link from the parent, and the link from - // the child as "..". - struct stat st_parent_after = {}; - ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); - EXPECT_EQ(st_parent_after.st_nlink, 3); - - // The child will have 1 link from the parent and 1 link which represents ".". - struct stat st_child = {}; - ASSERT_THAT(stat(sub_dir.path().c_str(), &st_child), SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st_child.st_mode)); - EXPECT_EQ(st_child.st_nlink, 2); - - // Finally delete the child dir and the parent link count should return to 2. - ASSERT_THAT(rmdir(sub_dir.path().c_str()), SyscallSucceeds()); - ASSERT_THAT(stat(dir.path().c_str(), &st_parent_after), SyscallSucceeds()); - - // Now we should only have links from the parent and "." since the subdir - // has been removed. - EXPECT_EQ(st_parent_after.st_nlink, 2); -} - -// Test statting a child of a non-directory. -TEST_F(StatTest, ChildOfNonDir) { - // Create a path that has a child of a regular file. - const std::string filename = JoinPath(test_file_name_, "child"); - - // Statting the path should return ENOTDIR. - struct stat st; - EXPECT_THAT(lstat(filename.c_str(), &st), SyscallFailsWithErrno(ENOTDIR)); -} - -// Test lstating a symlink directory. -TEST_F(StatTest, LstatSymlinkDir) { - // Create a directory and symlink to it. - const auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string symlink_to_dir = NewTempAbsPath(); - EXPECT_THAT(symlink(dir.path().c_str(), symlink_to_dir.c_str()), - SyscallSucceeds()); - auto cleanup = Cleanup([&symlink_to_dir]() { - EXPECT_THAT(unlink(symlink_to_dir.c_str()), SyscallSucceeds()); - }); - - // Lstat on the symlink should return symlink data. - struct stat st = {}; - ASSERT_THAT(lstat(symlink_to_dir.c_str(), &st), SyscallSucceeds()); - EXPECT_FALSE(S_ISDIR(st.st_mode)); - EXPECT_TRUE(S_ISLNK(st.st_mode)); - - // Lstat on the symlink with a trailing slash should return the directory - // data. - ASSERT_THAT(lstat(absl::StrCat(symlink_to_dir, "/").c_str(), &st), - SyscallSucceeds()); - EXPECT_TRUE(S_ISDIR(st.st_mode)); - EXPECT_FALSE(S_ISLNK(st.st_mode)); -} - -// Verify that we get an ELOOP from too many symbolic links even when there -// are directories in the middle. -TEST_F(StatTest, LstatELOOPPath) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - std::string subdir_base = "subdir"; - ASSERT_THAT(mkdir(JoinPath(dir.path(), subdir_base).c_str(), 0755), - SyscallSucceeds()); - - std::string target = JoinPath(dir.path(), subdir_base, subdir_base); - std::string dst = JoinPath("..", subdir_base); - ASSERT_THAT(symlink(dst.c_str(), target.c_str()), SyscallSucceeds()); - auto cleanup = Cleanup( - [&target]() { EXPECT_THAT(unlink(target.c_str()), SyscallSucceeds()); }); - - // Now build a path which is /subdir/subdir/... repeated many times so that - // we can build a path that is shorter than PATH_MAX but can still cause - // too many symbolic links. Note: Every other subdir is actually a directory - // so we're not in a situation where it's a -> b -> a -> b, where a and b - // are symbolic links. - std::string path = dir.path(); - std::string subdir_append = absl::StrCat("/", subdir_base); - do { - absl::StrAppend(&path, subdir_append); - // Keep appending /subdir until we would overflow PATH_MAX. - } while ((path.size() + subdir_append.size()) < PATH_MAX); - - struct stat s = {}; - ASSERT_THAT(lstat(path.c_str(), &s), SyscallFailsWithErrno(ELOOP)); -} - -TEST(SimpleStatTest, DifferentFilesHaveDifferentDeviceInodeNumberPairs) { - // TODO(gvisor.dev/issue/1624): This test case fails in VFS1 save/restore - // tests because VFS1 gofer inode number assignment restarts after - // save/restore, such that the inodes for file1 and file2 (which are - // unreferenced and therefore not retained in sentry checkpoints before the - // calls to lstat()) are assigned the same inode number. - SKIP_IF(IsRunningWithVFS1() && IsRunningWithSaveRestore()); - - TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - MaybeSave(); - struct stat st1 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file1.path())); - MaybeSave(); - struct stat st2 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file2.path())); - EXPECT_FALSE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) - << "both files have device number " << st1.st_dev << " and inode number " - << st1.st_ino; -} - -// Ensure that inode allocation for anonymous devices work correctly across -// save/restore. In particular, inode numbers should be unique across S/R. -TEST(SimpleStatTest, AnonDeviceAllocatesUniqueInodesAcrossSaveRestore) { - // Use sockets as a convenient way to create inodes on an anonymous device. - int fd; - ASSERT_THAT(fd = socket(AF_UNIX, SOCK_STREAM, 0), SyscallSucceeds()); - FileDescriptor fd1(fd); - MaybeSave(); - ASSERT_THAT(fd = socket(AF_UNIX, SOCK_STREAM, 0), SyscallSucceeds()); - FileDescriptor fd2(fd); - - struct stat st1; - struct stat st2; - ASSERT_THAT(fstat(fd1.get(), &st1), SyscallSucceeds()); - ASSERT_THAT(fstat(fd2.get(), &st2), SyscallSucceeds()); - - // The two fds should have different inode numbers. - EXPECT_NE(st2.st_ino, st1.st_ino); - - // Verify again after another S/R cycle. The inode numbers should remain the - // same. - MaybeSave(); - - struct stat st1_after; - struct stat st2_after; - ASSERT_THAT(fstat(fd1.get(), &st1_after), SyscallSucceeds()); - ASSERT_THAT(fstat(fd2.get(), &st2_after), SyscallSucceeds()); - - EXPECT_EQ(st1_after.st_ino, st1.st_ino); - EXPECT_EQ(st2_after.st_ino, st2.st_ino); -} - -#ifndef SYS_statx -#if defined(__x86_64__) -#define SYS_statx 332 -#elif defined(__aarch64__) -#define SYS_statx 291 -#else -#error "Unknown architecture" -#endif -#endif // SYS_statx - -#ifndef STATX_ALL -#define STATX_ALL 0x00000fffU -#endif // STATX_ALL - -// struct kernel_statx_timestamp is a Linux statx_timestamp struct. -struct kernel_statx_timestamp { - int64_t tv_sec; - uint32_t tv_nsec; - int32_t __reserved; -}; - -// struct kernel_statx is a Linux statx struct. Old versions of glibc do not -// expose it. See include/uapi/linux/stat.h -struct kernel_statx { - uint32_t stx_mask; - uint32_t stx_blksize; - uint64_t stx_attributes; - uint32_t stx_nlink; - uint32_t stx_uid; - uint32_t stx_gid; - uint16_t stx_mode; - uint16_t __spare0[1]; - uint64_t stx_ino; - uint64_t stx_size; - uint64_t stx_blocks; - uint64_t stx_attributes_mask; - struct kernel_statx_timestamp stx_atime; - struct kernel_statx_timestamp stx_btime; - struct kernel_statx_timestamp stx_ctime; - struct kernel_statx_timestamp stx_mtime; - uint32_t stx_rdev_major; - uint32_t stx_rdev_minor; - uint32_t stx_dev_major; - uint32_t stx_dev_minor; - uint64_t __spare2[14]; -}; - -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); -} - -TEST_F(StatTest, StatxAbsPath) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - struct kernel_statx stx; - EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, STATX_ALL, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(stx.stx_mode)); -} - -TEST_F(StatTest, StatxRelPathDirFD) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - struct kernel_statx stx; - auto const dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); - auto filename = std::string(Basename(test_file_name_)); - - EXPECT_THAT(statx(dirfd.get(), filename.c_str(), 0, STATX_ALL, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(stx.stx_mode)); -} - -TEST_F(StatTest, StatxRelPathCwd) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - auto filename = std::string(Basename(test_file_name_)); - struct kernel_statx stx; - EXPECT_THAT(statx(AT_FDCWD, filename.c_str(), 0, STATX_ALL, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(stx.stx_mode)); -} - -TEST_F(StatTest, StatxEmptyPath) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - const auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY)); - struct kernel_statx stx; - EXPECT_THAT(statx(fd.get(), "", AT_EMPTY_PATH, STATX_ALL, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(stx.stx_mode)); -} - -TEST_F(StatTest, StatxDoesNotRejectExtraneousMaskBits) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - struct kernel_statx stx; - // Set all mask bits except for STATX__RESERVED. - uint mask = 0xffffffff & ~0x80000000; - EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, mask, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(stx.stx_mode)); -} - -TEST_F(StatTest, StatxRejectsReservedMaskBit) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - struct kernel_statx stx; - // Set STATX__RESERVED in the mask. - EXPECT_THAT(statx(-1, test_file_name_.c_str(), 0, 0x80000000, &stx), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(StatTest, StatxSymlink) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - std::string parent_dir = "/tmp"; - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(parent_dir, test_file_name_)); - std::string p = link.path(); - - struct kernel_statx stx; - EXPECT_THAT(statx(AT_FDCWD, p.c_str(), AT_SYMLINK_NOFOLLOW, STATX_ALL, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISLNK(stx.stx_mode)); - EXPECT_THAT(statx(AT_FDCWD, p.c_str(), 0, STATX_ALL, &stx), - SyscallSucceeds()); - EXPECT_TRUE(S_ISREG(stx.stx_mode)); -} - -TEST_F(StatTest, StatxInvalidFlags) { - SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 && - errno == ENOSYS); - - struct kernel_statx stx; - EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), 12345, 0, &stx), - SyscallFailsWithErrno(EINVAL)); - - // Sync flags are mutually exclusive. - EXPECT_THAT(statx(AT_FDCWD, test_file_name_.c_str(), - AT_STATX_FORCE_SYNC | AT_STATX_DONT_SYNC, 0, &stx), - SyscallFailsWithErrno(EINVAL)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/stat_times.cc b/test/syscalls/linux/stat_times.cc deleted file mode 100644 index 68c0bef09..000000000 --- a/test/syscalls/linux/stat_times.cc +++ /dev/null @@ -1,303 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/stat.h> - -#include <tuple> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::testing::IsEmpty; -using ::testing::Not; - -std::tuple<absl::Time, absl::Time, absl::Time> GetTime(const TempPath& file) { - struct stat statbuf = {}; - EXPECT_THAT(stat(file.path().c_str(), &statbuf), SyscallSucceeds()); - - const auto atime = absl::TimeFromTimespec(statbuf.st_atim); - const auto mtime = absl::TimeFromTimespec(statbuf.st_mtim); - const auto ctime = absl::TimeFromTimespec(statbuf.st_ctim); - return std::make_tuple(atime, mtime, ctime); -} - -enum class AtimeEffect { - Unchanged, - Changed, -}; - -enum class MtimeEffect { - Unchanged, - Changed, -}; - -enum class CtimeEffect { - Unchanged, - Changed, -}; - -// Tests that fn modifies the atime/mtime/ctime of path as specified. -void CheckTimes(const TempPath& path, std::function<void()> fn, - AtimeEffect atime_effect, MtimeEffect mtime_effect, - CtimeEffect ctime_effect) { - absl::Time atime, mtime, ctime; - std::tie(atime, mtime, ctime) = GetTime(path); - - // FIXME(b/132819225): gVisor filesystem timestamps inconsistently use the - // internal or host clock, which may diverge slightly. Allow some slack on - // times to account for the difference. - // - // Here we sleep for 1s so that initial creation of path doesn't fall within - // the before slack window. - absl::SleepFor(absl::Seconds(1)); - - const absl::Time before = absl::Now() - absl::Seconds(1); - - // Perform the op. - fn(); - - const absl::Time after = absl::Now() + absl::Seconds(1); - - absl::Time atime2, mtime2, ctime2; - std::tie(atime2, mtime2, ctime2) = GetTime(path); - - if (atime_effect == AtimeEffect::Changed) { - EXPECT_LE(before, atime2); - EXPECT_GE(after, atime2); - EXPECT_GT(atime2, atime); - } else { - EXPECT_EQ(atime2, atime); - } - - if (mtime_effect == MtimeEffect::Changed) { - EXPECT_LE(before, mtime2); - EXPECT_GE(after, mtime2); - EXPECT_GT(mtime2, mtime); - } else { - EXPECT_EQ(mtime2, mtime); - } - - if (ctime_effect == CtimeEffect::Changed) { - EXPECT_LE(before, ctime2); - EXPECT_GE(after, ctime2); - EXPECT_GT(ctime2, ctime); - } else { - EXPECT_EQ(ctime2, ctime); - } -} - -// File creation time is reflected in atime, mtime, and ctime. -TEST(StatTimesTest, FileCreation) { - const DisableSave ds; // Timing-related test. - - // Get a time for when the file is created. - // - // FIXME(b/132819225): See above. - const absl::Time before = absl::Now() - absl::Seconds(1); - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const absl::Time after = absl::Now() + absl::Seconds(1); - - absl::Time atime, mtime, ctime; - std::tie(atime, mtime, ctime) = GetTime(file); - - EXPECT_LE(before, atime); - EXPECT_LE(before, mtime); - EXPECT_LE(before, ctime); - EXPECT_GE(after, atime); - EXPECT_GE(after, mtime); - EXPECT_GE(after, ctime); -} - -// Calling chmod on a file changes ctime. -TEST(StatTimesTest, FileChmod) { - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - auto fn = [&] { - EXPECT_THAT(chmod(file.path().c_str(), 0666), SyscallSucceeds()); - }; - CheckTimes(file, fn, AtimeEffect::Unchanged, MtimeEffect::Unchanged, - CtimeEffect::Changed); -} - -// Renaming a file changes ctime. -TEST(StatTimesTest, FileRename) { - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - const std::string newpath = NewTempAbsPath(); - - auto fn = [&] { - ASSERT_THAT(rename(file.release().c_str(), newpath.c_str()), - SyscallSucceeds()); - file.reset(newpath); - }; - CheckTimes(file, fn, AtimeEffect::Unchanged, MtimeEffect::Unchanged, - CtimeEffect::Changed); -} - -// Renaming a file changes ctime, even with an open FD. -// -// NOTE(b/132732387): This is a regression test for fs/gofer failing to update -// cached ctime. -TEST(StatTimesTest, FileRenameOpenFD) { - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // Holding an FD shouldn't affect behavior. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); - - const std::string newpath = NewTempAbsPath(); - - // FIXME(b/132814682): Restore fails with an uncached gofer and an open FD - // across rename. - // - // N.B. The logic here looks backwards because it isn't possible to - // conditionally disable save, only conditionally re-enable it. - DisableSave ds; - if (!getenv("GVISOR_GOFER_UNCACHED")) { - ds.reset(); - } - - auto fn = [&] { - ASSERT_THAT(rename(file.release().c_str(), newpath.c_str()), - SyscallSucceeds()); - file.reset(newpath); - }; - CheckTimes(file, fn, AtimeEffect::Unchanged, MtimeEffect::Unchanged, - CtimeEffect::Changed); -} - -// Calling utimes on a file changes ctime and the time that we ask to change -// (atime to now in this case). -TEST(StatTimesTest, FileUtimes) { - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - auto fn = [&] { - const struct timespec ts[2] = {{0, UTIME_NOW}, {0, UTIME_OMIT}}; - ASSERT_THAT(utimensat(AT_FDCWD, file.path().c_str(), ts, 0), - SyscallSucceeds()); - }; - CheckTimes(file, fn, AtimeEffect::Changed, MtimeEffect::Unchanged, - CtimeEffect::Changed); -} - -// Truncating a file changes mtime and ctime. -TEST(StatTimesTest, FileTruncate) { - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "yaaass", 0666)); - - auto fn = [&] { - EXPECT_THAT(truncate(file.path().c_str(), 0), SyscallSucceeds()); - }; - CheckTimes(file, fn, AtimeEffect::Unchanged, MtimeEffect::Changed, - CtimeEffect::Changed); -} - -// Writing a file changes mtime and ctime. -TEST(StatTimesTest, FileWrite) { - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "yaaass", 0666)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0)); - - auto fn = [&] { - const std::string contents = "all the single dollars"; - EXPECT_THAT(WriteFd(fd.get(), contents.data(), contents.size()), - SyscallSucceeds()); - }; - CheckTimes(file, fn, AtimeEffect::Unchanged, MtimeEffect::Changed, - CtimeEffect::Changed); -} - -// Reading a file changes atime. -TEST(StatTimesTest, FileRead) { - const std::string contents = "bills bills bills"; - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), contents, 0666)); - - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY, 0)); - - auto fn = [&] { - char buf[20]; - ASSERT_THAT(ReadFd(fd.get(), buf, sizeof(buf)), - SyscallSucceedsWithValue(contents.size())); - }; - CheckTimes(file, fn, AtimeEffect::Changed, MtimeEffect::Unchanged, - CtimeEffect::Unchanged); -} - -// Listing files in a directory changes atime. -TEST(StatTimesTest, DirList) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - - auto fn = [&] { - const auto contents = ASSERT_NO_ERRNO_AND_VALUE(ListDir(dir.path(), false)); - EXPECT_THAT(contents, Not(IsEmpty())); - }; - CheckTimes(dir, fn, AtimeEffect::Changed, MtimeEffect::Unchanged, - CtimeEffect::Unchanged); -} - -// Creating a file in a directory changes mtime and ctime. -TEST(StatTimesTest, DirCreateFile) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - TempPath file; - auto fn = [&] { - file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - }; - CheckTimes(dir, fn, AtimeEffect::Unchanged, MtimeEffect::Changed, - CtimeEffect::Changed); -} - -// Creating a directory in a directory changes mtime and ctime. -TEST(StatTimesTest, DirCreateDir) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - TempPath dir2; - auto fn = [&] { - dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - }; - CheckTimes(dir, fn, AtimeEffect::Unchanged, MtimeEffect::Changed, - CtimeEffect::Changed); -} - -// Removing a file from a directory changes mtime and ctime. -TEST(StatTimesTest, DirRemoveFile) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - auto fn = [&] { file.reset(); }; - CheckTimes(dir, fn, AtimeEffect::Unchanged, MtimeEffect::Changed, - CtimeEffect::Changed); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/statfs.cc b/test/syscalls/linux/statfs.cc deleted file mode 100644 index d4ea8e026..000000000 --- a/test/syscalls/linux/statfs.cc +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <linux/magic.h> -#include <sys/statfs.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(StatfsTest, CannotStatBadPath) { - auto temp_file = NewTempAbsPathInDir("/tmp"); - - struct statfs st; - EXPECT_THAT(statfs(temp_file.c_str(), &st), SyscallFailsWithErrno(ENOENT)); -} - -TEST(StatfsTest, InternalTmpfs) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - struct statfs st; - EXPECT_THAT(statfs(temp_file.path().c_str(), &st), SyscallSucceeds()); -} - -TEST(StatfsTest, InternalDevShm) { - struct statfs st; - EXPECT_THAT(statfs("/dev/shm", &st), SyscallSucceeds()); - - // This assumes that /dev/shm is tmpfs. - // Note: We could be an overlay on some configurations. - EXPECT_TRUE(st.f_type == TMPFS_MAGIC || st.f_type == OVERLAYFS_SUPER_MAGIC); -} - -TEST(FstatfsTest, CannotStatBadFd) { - struct statfs st; - EXPECT_THAT(fstatfs(-1, &st), SyscallFailsWithErrno(EBADF)); -} - -TEST(FstatfsTest, InternalTmpfs) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); - - struct statfs st; - EXPECT_THAT(fstatfs(fd.get(), &st), SyscallSucceeds()); -} - -TEST(FstatfsTest, CanStatFileWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_PATH)); - - struct statfs st; - EXPECT_THAT(fstatfs(fd.get(), &st), SyscallSucceeds()); -} - -TEST(FstatfsTest, InternalDevShm) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/shm", O_RDONLY)); - - struct statfs st; - EXPECT_THAT(fstatfs(fd.get(), &st), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sticky.cc b/test/syscalls/linux/sticky.cc deleted file mode 100644 index 4afed6d08..000000000 --- a/test/syscalls/linux/sticky.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <grp.h> -#include <sys/prctl.h> -#include <sys/types.h> -#include <unistd.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -ABSL_FLAG(int32_t, scratch_uid, 65534, "first scratch UID"); -ABSL_FLAG(int32_t, scratch_gid, 65534, "first scratch GID"); - -namespace gvisor { -namespace testing { - -namespace { - -TEST(StickyTest, StickyBitPermDenied) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(chmod(parent.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); - - // After changing credentials below, we need to use an open fd to make - // modifications in the parent dir, because there is no guarantee that we will - // still have the ability to open it. - const FileDescriptor parent_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent.path(), O_DIRECTORY)); - ASSERT_THAT(openat(parent_fd.get(), "file", O_CREAT), SyscallSucceeds()); - ASSERT_THAT(mkdirat(parent_fd.get(), "dir", 0777), SyscallSucceeds()); - ASSERT_THAT(symlinkat("xyz", parent_fd.get(), "link"), SyscallSucceeds()); - - // Drop privileges and change IDs only in child thread, or else this parent - // thread won't be able to open some log files after the test ends. - ScopedThread([&] { - // Drop privileges. - if (HaveCapability(CAP_FOWNER).ValueOrDie()) { - EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, false)); - } - - // Change EUID and EGID. - EXPECT_THAT( - syscall(SYS_setresgid, -1, absl::GetFlag(FLAGS_scratch_gid), -1), - SyscallSucceeds()); - EXPECT_THAT( - syscall(SYS_setresuid, -1, absl::GetFlag(FLAGS_scratch_uid), -1), - SyscallSucceeds()); - - EXPECT_THAT(renameat(parent_fd.get(), "file", parent_fd.get(), "file2"), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(unlinkat(parent_fd.get(), "file", 0), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(unlinkat(parent_fd.get(), "dir", AT_REMOVEDIR), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(unlinkat(parent_fd.get(), "link", 0), - SyscallFailsWithErrno(EPERM)); - }); -} - -TEST(StickyTest, StickyBitSameUID) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(chmod(parent.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); - - // After changing credentials below, we need to use an open fd to make - // modifications in the parent dir, because there is no guarantee that we will - // still have the ability to open it. - const FileDescriptor parent_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent.path(), O_DIRECTORY)); - ASSERT_THAT(openat(parent_fd.get(), "file", O_CREAT), SyscallSucceeds()); - ASSERT_THAT(mkdirat(parent_fd.get(), "dir", 0777), SyscallSucceeds()); - ASSERT_THAT(symlinkat("xyz", parent_fd.get(), "link"), SyscallSucceeds()); - - // Drop privileges and change IDs only in child thread, or else this parent - // thread won't be able to open some log files after the test ends. - ScopedThread([&] { - // Drop privileges. - if (HaveCapability(CAP_FOWNER).ValueOrDie()) { - EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, false)); - } - - // Change EGID. - EXPECT_THAT( - syscall(SYS_setresgid, -1, absl::GetFlag(FLAGS_scratch_gid), -1), - SyscallSucceeds()); - - // We still have the same EUID. - EXPECT_THAT(renameat(parent_fd.get(), "file", parent_fd.get(), "file2"), - SyscallSucceeds()); - EXPECT_THAT(unlinkat(parent_fd.get(), "file2", 0), SyscallSucceeds()); - EXPECT_THAT(unlinkat(parent_fd.get(), "dir", AT_REMOVEDIR), - SyscallSucceeds()); - EXPECT_THAT(unlinkat(parent_fd.get(), "link", 0), SyscallSucceeds()); - }); -} - -TEST(StickyTest, StickyBitCapFOWNER) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); - - const TempPath parent = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(chmod(parent.path().c_str(), 0777 | S_ISVTX), SyscallSucceeds()); - - // After changing credentials below, we need to use an open fd to make - // modifications in the parent dir, because there is no guarantee that we will - // still have the ability to open it. - const FileDescriptor parent_fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(parent.path(), O_DIRECTORY)); - ASSERT_THAT(openat(parent_fd.get(), "file", O_CREAT), SyscallSucceeds()); - ASSERT_THAT(mkdirat(parent_fd.get(), "dir", 0777), SyscallSucceeds()); - ASSERT_THAT(symlinkat("xyz", parent_fd.get(), "link"), SyscallSucceeds()); - - // Drop privileges and change IDs only in child thread, or else this parent - // thread won't be able to open some log files after the test ends. - ScopedThread([&] { - // Set PR_SET_KEEPCAPS. - EXPECT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); - - // Change EUID and EGID. - EXPECT_THAT( - syscall(SYS_setresgid, -1, absl::GetFlag(FLAGS_scratch_gid), -1), - SyscallSucceeds()); - EXPECT_THAT( - syscall(SYS_setresuid, -1, absl::GetFlag(FLAGS_scratch_uid), -1), - SyscallSucceeds()); - - EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, true)); - EXPECT_THAT(renameat(parent_fd.get(), "file", parent_fd.get(), "file2"), - SyscallSucceeds()); - EXPECT_THAT(unlinkat(parent_fd.get(), "file2", 0), SyscallSucceeds()); - EXPECT_THAT(unlinkat(parent_fd.get(), "dir", AT_REMOVEDIR), - SyscallSucceeds()); - EXPECT_THAT(unlinkat(parent_fd.get(), "link", 0), SyscallSucceeds()); - }); -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/symlink.cc b/test/syscalls/linux/symlink.cc deleted file mode 100644 index ea219a091..000000000 --- a/test/syscalls/linux/symlink.cc +++ /dev/null @@ -1,472 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -mode_t FilePermission(const std::string& path) { - struct stat buf = {0}; - TEST_CHECK(lstat(path.c_str(), &buf) == 0); - return buf.st_mode & 0777; -} - -// Test that name collisions are checked on the new link path, not the source -// path. Regression test for b/31782115. -TEST(SymlinkTest, CanCreateSymlinkWithCachedSourceDirent) { - const std::string srcname = NewTempAbsPath(); - const std::string newname = NewTempAbsPath(); - const std::string basedir = std::string(Dirname(srcname)); - ASSERT_EQ(basedir, Dirname(newname)); - - ASSERT_THAT(chdir(basedir.c_str()), SyscallSucceeds()); - - // Open the source node to cause the underlying dirent to be cached. It will - // remain cached while we have the file open. - int fd; - ASSERT_THAT(fd = open(srcname.c_str(), O_CREAT | O_RDWR, 0666), - SyscallSucceeds()); - FileDescriptor fd_closer(fd); - - // Attempt to create a symlink. If the bug exists, this will fail since the - // dirent link creation code will check for a name collision on the source - // link name. - EXPECT_THAT(symlink(std::string(Basename(srcname)).c_str(), - std::string(Basename(newname)).c_str()), - SyscallSucceeds()); -} - -TEST(SymlinkTest, CanCreateSymlinkFile) { - const std::string oldname = NewTempAbsPath(); - const std::string newname = NewTempAbsPath(); - - int fd; - ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); - EXPECT_EQ(FilePermission(newname), 0777); - - auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(newname)); - EXPECT_EQ(oldname, link); - - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, CanCreateSymlinkDir) { - const std::string olddir = NewTempAbsPath(); - const std::string newdir = NewTempAbsPath(); - - EXPECT_THAT(mkdir(olddir.c_str(), 0777), SyscallSucceeds()); - EXPECT_THAT(symlink(olddir.c_str(), newdir.c_str()), SyscallSucceeds()); - EXPECT_EQ(FilePermission(newdir), 0777); - - auto link = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(newdir)); - EXPECT_EQ(olddir, link); - - EXPECT_THAT(unlink(newdir.c_str()), SyscallSucceeds()); - - ASSERT_THAT(rmdir(olddir.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, CannotCreateSymlinkInReadOnlyDir) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - const std::string olddir = NewTempAbsPath(); - ASSERT_THAT(mkdir(olddir.c_str(), 0444), SyscallSucceeds()); - - const std::string newdir = NewTempAbsPathInDir(olddir); - EXPECT_THAT(symlink(olddir.c_str(), newdir.c_str()), - SyscallFailsWithErrno(EACCES)); - - ASSERT_THAT(rmdir(olddir.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, CannotSymlinkOverExistingFile) { - const auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const auto newfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - EXPECT_THAT(symlink(oldfile.path().c_str(), newfile.path().c_str()), - SyscallFailsWithErrno(EEXIST)); -} - -TEST(SymlinkTest, CannotSymlinkOverExistingDir) { - const auto oldfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const auto newdir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - EXPECT_THAT(symlink(oldfile.path().c_str(), newdir.path().c_str()), - SyscallFailsWithErrno(EEXIST)); -} - -TEST(SymlinkTest, OldnameIsEmpty) { - const std::string newname = NewTempAbsPath(); - EXPECT_THAT(symlink("", newname.c_str()), SyscallFailsWithErrno(ENOENT)); -} - -TEST(SymlinkTest, OldnameIsDangling) { - const std::string newname = NewTempAbsPath(); - EXPECT_THAT(symlink("/dangling", newname.c_str()), SyscallSucceeds()); - - // This is required for S/R random save tests, which pre-run this test - // in the same TEST_TMPDIR, which means that we need to clean it for any - // operations exclusively creating files, like symlink above. - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, NewnameCannotExist) { - const std::string newname = - JoinPath(GetAbsoluteTestTmpdir(), "thisdoesnotexist", "foo"); - EXPECT_THAT(symlink("/thisdoesnotmatter", newname.c_str()), - SyscallFailsWithErrno(ENOENT)); -} - -TEST(SymlinkTest, CanEvaluateLink) { - const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - - // We are going to assert that the symlink inode id is the same as the linked - // file's inode id. In order for the inode id to be stable across - // save/restore, it must be kept open. The FileDescriptor type will do that - // for us automatically. - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR)); - struct stat file_st; - EXPECT_THAT(fstat(fd.get(), &file_st), SyscallSucceeds()); - - const std::string link = NewTempAbsPath(); - EXPECT_THAT(symlink(file.path().c_str(), link.c_str()), SyscallSucceeds()); - EXPECT_EQ(FilePermission(link), 0777); - - auto linkfd = ASSERT_NO_ERRNO_AND_VALUE(Open(link.c_str(), O_RDWR)); - struct stat link_st; - EXPECT_THAT(fstat(linkfd.get(), &link_st), SyscallSucceeds()); - - // Check that in fact newname points to the file we expect. - EXPECT_EQ(file_st.st_dev, link_st.st_dev); - EXPECT_EQ(file_st.st_ino, link_st.st_ino); -} - -TEST(SymlinkTest, TargetIsNotMapped) { - const std::string oldname = NewTempAbsPath(); - const std::string newname = NewTempAbsPath(); - - int fd; - // Create the target so that when we read the link, it exists. - ASSERT_THAT(fd = open(oldname.c_str(), O_CREAT | O_RDWR, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - // Create a symlink called newname that points to oldname. - EXPECT_THAT(symlink(oldname.c_str(), newname.c_str()), SyscallSucceeds()); - - std::vector<char> buf(1024); - int linksize; - // Read the link and assert that the oldname is still the same. - EXPECT_THAT(linksize = readlink(newname.c_str(), buf.data(), 1024), - SyscallSucceeds()); - EXPECT_EQ(0, strncmp(oldname.c_str(), buf.data(), linksize)); - - EXPECT_THAT(unlink(newname.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(oldname.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, PreadFromSymlink) { - std::string name = NewTempAbsPath(); - int fd; - ASSERT_THAT(fd = open(name.c_str(), O_CREAT, 0644), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - - std::string linkname = NewTempAbsPath(); - ASSERT_THAT(symlink(name.c_str(), linkname.c_str()), SyscallSucceeds()); - - ASSERT_THAT(fd = open(linkname.c_str(), O_RDONLY), SyscallSucceeds()); - - char buf[1024]; - EXPECT_THAT(pread64(fd, buf, 1024, 0), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - - EXPECT_THAT(unlink(name.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(linkname.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, PwriteToSymlink) { - std::string name = NewTempAbsPath(); - int fd; - ASSERT_THAT(fd = open(name.c_str(), O_CREAT, 0644), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - - std::string linkname = NewTempAbsPath(); - ASSERT_THAT(symlink(name.c_str(), linkname.c_str()), SyscallSucceeds()); - - ASSERT_THAT(fd = open(linkname.c_str(), O_WRONLY), SyscallSucceeds()); - - const int data_size = 10; - const std::string data = std::string(data_size, 'a'); - EXPECT_THAT(pwrite64(fd, data.c_str(), data.size(), 0), - SyscallSucceedsWithValue(data.size())); - - ASSERT_THAT(close(fd), SyscallSucceeds()); - ASSERT_THAT(fd = open(name.c_str(), O_RDONLY), SyscallSucceeds()); - - char buf[data_size + 1]; - EXPECT_THAT(pread64(fd, buf, data.size(), 0), SyscallSucceeds()); - buf[data.size()] = '\0'; - EXPECT_STREQ(buf, data.c_str()); - - ASSERT_THAT(close(fd), SyscallSucceeds()); - - EXPECT_THAT(unlink(name.c_str()), SyscallSucceeds()); - EXPECT_THAT(unlink(linkname.c_str()), SyscallSucceeds()); -} - -TEST(SymlinkTest, SymlinkAtDegradedPermissions_NoRandomSave) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - - int dirfd; - ASSERT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), - SyscallSucceeds()); - - const DisableSave ds; // Permissions are dropped. - EXPECT_THAT(fchmod(dirfd, 0), SyscallSucceeds()); - - std::string basename = std::string(Basename(file.path())); - EXPECT_THAT(symlinkat("/dangling", dirfd, basename.c_str()), - SyscallFailsWithErrno(EACCES)); - EXPECT_THAT(close(dirfd), SyscallSucceeds()); -} - -TEST(SymlinkTest, SymlinkAtDirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string filepath = NewTempAbsPathInDir(dir.path()); - const std::string base = std::string(Basename(filepath)); - FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path().c_str(), O_DIRECTORY | O_PATH)); - - EXPECT_THAT(symlinkat("/dangling", dirfd.get(), base.c_str()), - SyscallSucceeds()); -} - -TEST(SymlinkTest, ReadlinkAtDirWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string filepath = NewTempAbsPathInDir(dir.path()); - const std::string base = std::string(Basename(filepath)); - ASSERT_THAT(symlink("/dangling", filepath.c_str()), SyscallSucceeds()); - - FileDescriptor dirfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path().c_str(), O_DIRECTORY | O_PATH)); - - std::vector<char> buf(1024); - int linksize; - EXPECT_THAT( - linksize = readlinkat(dirfd.get(), base.c_str(), buf.data(), 1024), - SyscallSucceeds()); - EXPECT_EQ(0, strncmp("/dangling", buf.data(), linksize)); -} - -TEST(SymlinkTest, ReadlinkAtDegradedPermissions_NoRandomSave) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string oldpath = NewTempAbsPathInDir(dir.path()); - const std::string oldbase = std::string(Basename(oldpath)); - ASSERT_THAT(symlink("/dangling", oldpath.c_str()), SyscallSucceeds()); - - int dirfd; - EXPECT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), - SyscallSucceeds()); - - const DisableSave ds; // Permissions are dropped. - EXPECT_THAT(fchmod(dirfd, 0), SyscallSucceeds()); - - char buf[1024]; - int linksize; - EXPECT_THAT(linksize = readlinkat(dirfd, oldbase.c_str(), buf, 1024), - SyscallFailsWithErrno(EACCES)); - EXPECT_THAT(close(dirfd), SyscallSucceeds()); -} - -TEST(SymlinkTest, ChmodSymlink) { - auto target = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string newpath = NewTempAbsPath(); - ASSERT_THAT(symlink(target.path().c_str(), newpath.c_str()), - SyscallSucceeds()); - EXPECT_EQ(FilePermission(newpath), 0777); - EXPECT_THAT(chmod(newpath.c_str(), 0666), SyscallSucceeds()); - EXPECT_EQ(FilePermission(newpath), 0777); -} - -// Test that following a symlink updates the atime on the symlink. -TEST(SymlinkTest, FollowUpdatesATime) { - const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string link = NewTempAbsPath(); - EXPECT_THAT(symlink(file.path().c_str(), link.c_str()), SyscallSucceeds()); - - // Lstat the symlink. - struct stat st_before_follow; - ASSERT_THAT(lstat(link.c_str(), &st_before_follow), SyscallSucceeds()); - - // Let the clock advance. - absl::SleepFor(absl::Seconds(1)); - - // Open the file via the symlink. - int fd; - ASSERT_THAT(fd = open(link.c_str(), O_RDWR, 0666), SyscallSucceeds()); - FileDescriptor fd_closer(fd); - - // Lstat the symlink again, and check that atime is updated. - struct stat st_after_follow; - ASSERT_THAT(lstat(link.c_str(), &st_after_follow), SyscallSucceeds()); - EXPECT_LT(st_before_follow.st_atime, st_after_follow.st_atime); -} - -TEST(SymlinkTest, SymlinkAtEmptyPath) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - auto fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY, 0666)); - EXPECT_THAT(symlinkat(file.path().c_str(), fd.get(), ""), - SyscallFailsWithErrno(ENOENT)); -} - -class ParamSymlinkTest : public ::testing::TestWithParam<std::string> {}; - -// Test that creating an existing symlink with creat will create the target. -TEST_P(ParamSymlinkTest, CreatLinkCreatesTarget) { - const std::string target = GetParam(); - const std::string linkpath = NewTempAbsPath(); - - ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); - - int fd; - EXPECT_THAT(fd = creat(linkpath.c_str(), 0666), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - struct stat st; - EXPECT_THAT(stat(target.c_str(), &st), SyscallSucceeds()); - - ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); - ASSERT_THAT(unlink(target.c_str()), SyscallSucceeds()); -} - -// Test that opening an existing symlink with O_CREAT will create the target. -TEST_P(ParamSymlinkTest, OpenLinkCreatesTarget) { - const std::string target = GetParam(); - const std::string linkpath = NewTempAbsPath(); - - ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); - - int fd; - EXPECT_THAT(fd = open(linkpath.c_str(), O_CREAT, 0666), SyscallSucceeds()); - ASSERT_THAT(close(fd), SyscallSucceeds()); - - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - struct stat st; - EXPECT_THAT(stat(target.c_str(), &st), SyscallSucceeds()); - - ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); - ASSERT_THAT(unlink(target.c_str()), SyscallSucceeds()); -} - -// Test that opening a self-symlink with O_CREAT will fail with ELOOP. -TEST_P(ParamSymlinkTest, CreateExistingSelfLink) { - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - - const std::string linkpath = GetParam(); - ASSERT_THAT(symlink(linkpath.c_str(), linkpath.c_str()), SyscallSucceeds()); - - EXPECT_THAT(open(linkpath.c_str(), O_CREAT, 0666), - SyscallFailsWithErrno(ELOOP)); - - ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); -} - -// Test that opening a file that is a symlink to its parent directory fails -// with ELOOP. -TEST_P(ParamSymlinkTest, CreateExistingParentLink) { - ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds()); - - const std::string linkpath = GetParam(); - const std::string target = JoinPath(linkpath, "child"); - ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); - - EXPECT_THAT(open(linkpath.c_str(), O_CREAT, 0666), - SyscallFailsWithErrno(ELOOP)); - - ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); -} - -// Test that opening an existing symlink with O_CREAT|O_EXCL will fail with -// EEXIST. -TEST_P(ParamSymlinkTest, OpenLinkExclFails) { - const std::string target = GetParam(); - const std::string linkpath = NewTempAbsPath(); - - ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); - - EXPECT_THAT(open(linkpath.c_str(), O_CREAT | O_EXCL, 0666), - SyscallFailsWithErrno(EEXIST)); - - ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); -} - -// Test that opening an existing symlink with O_CREAT|O_NOFOLLOW will fail with -// ELOOP. -TEST_P(ParamSymlinkTest, OpenLinkNoFollowFails) { - const std::string target = GetParam(); - const std::string linkpath = NewTempAbsPath(); - - ASSERT_THAT(symlink(target.c_str(), linkpath.c_str()), SyscallSucceeds()); - - EXPECT_THAT(open(linkpath.c_str(), O_CREAT | O_NOFOLLOW, 0666), - SyscallFailsWithErrno(ELOOP)); - - ASSERT_THAT(unlink(linkpath.c_str()), SyscallSucceeds()); -} - -INSTANTIATE_TEST_SUITE_P(AbsAndRelTarget, ParamSymlinkTest, - ::testing::Values(NewTempAbsPath(), NewTempRelPath())); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sync.cc b/test/syscalls/linux/sync.cc deleted file mode 100644 index 84a2c4ed7..000000000 --- a/test/syscalls/linux/sync.cc +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <stdio.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SyncTest, SyncEverything) { - ASSERT_THAT(syscall(SYS_sync), SyscallSucceeds()); -} - -TEST(SyncTest, SyncFileSytem) { - int fd; - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - ASSERT_THAT(fd = open(f.path().c_str(), O_RDONLY), SyscallSucceeds()); - EXPECT_THAT(syncfs(fd), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(SyncTest, SyncFromPipe) { - int pipes[2]; - EXPECT_THAT(pipe(pipes), SyscallSucceeds()); - EXPECT_THAT(syncfs(pipes[0]), SyscallSucceeds()); - EXPECT_THAT(syncfs(pipes[1]), SyscallSucceeds()); - EXPECT_THAT(close(pipes[0]), SyscallSucceeds()); - EXPECT_THAT(close(pipes[1]), SyscallSucceeds()); -} - -TEST(SyncTest, CannotSyncFileSystemAtBadFd) { - EXPECT_THAT(syncfs(-1), SyscallFailsWithErrno(EBADF)); -} - -TEST(SyncTest, CannotSyncFileSystemAtOpathFD) { - SKIP_IF(IsRunningWithVFS1()); - - const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); - - EXPECT_THAT(syncfs(fd.get()), SyscallFailsWithErrno(EBADF)); -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sync_file_range.cc b/test/syscalls/linux/sync_file_range.cc deleted file mode 100644 index 36cc42043..000000000 --- a/test/syscalls/linux/sync_file_range.cc +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <stdio.h> -#include <unistd.h> - -#include <string> - -#include "gtest/gtest.h" -#include "test/util/file_descriptor.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SyncFileRangeTest, TempFileSucceeds) { - auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); - constexpr char data[] = "some data to sync"; - int fd = f.get(); - - EXPECT_THAT(write(fd, data, sizeof(data)), - SyscallSucceedsWithValue(sizeof(data))); - EXPECT_THAT(sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE), - SyscallSucceeds()); - EXPECT_THAT(sync_file_range(fd, 0, 0, 0), SyscallSucceeds()); - EXPECT_THAT( - sync_file_range(fd, 0, 0, - SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER | - SYNC_FILE_RANGE_WAIT_BEFORE), - SyscallSucceeds()); - EXPECT_THAT(sync_file_range( - fd, 0, 1, SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER), - SyscallSucceeds()); - EXPECT_THAT(sync_file_range( - fd, 1, 0, SYNC_FILE_RANGE_WRITE | SYNC_FILE_RANGE_WAIT_AFTER), - SyscallSucceeds()); -} - -TEST(SyncFileRangeTest, CannotSyncFileRangeOnUnopenedFd) { - auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); - constexpr char data[] = "some data to sync"; - int fd = f.get(); - - EXPECT_THAT(write(fd, data, sizeof(data)), - SyscallSucceedsWithValue(sizeof(data))); - - pid_t pid = fork(); - if (pid == 0) { - f.reset(); - - // fd is now invalid. - TEST_CHECK(sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WRITE) == -1); - TEST_PCHECK(errno == EBADF); - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - - int status = 0; - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(WEXITSTATUS(status), 0); -} - -TEST(SyncFileRangeTest, BadArgs) { - auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); - int fd = f.get(); - - EXPECT_THAT(sync_file_range(fd, -1, 0, 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(sync_file_range(fd, 0, -1, 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(sync_file_range(fd, 8912, INT64_MAX - 4096, 0), - SyscallFailsWithErrno(EINVAL)); -} - -TEST(SyncFileRangeTest, CannotSyncFileRangeWithWaitBefore) { - auto tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path(), O_RDWR)); - constexpr char data[] = "some data to sync"; - int fd = f.get(); - - EXPECT_THAT(write(fd, data, sizeof(data)), - SyscallSucceedsWithValue(sizeof(data))); - if (IsRunningOnGvisor()) { - EXPECT_THAT(sync_file_range(fd, 0, 0, SYNC_FILE_RANGE_WAIT_BEFORE), - SyscallFailsWithErrno(ENOSYS)); - EXPECT_THAT( - sync_file_range(fd, 0, 0, - SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE), - SyscallFailsWithErrno(ENOSYS)); - } -} - -} // namespace -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sysinfo.cc b/test/syscalls/linux/sysinfo.cc deleted file mode 100644 index 1a71256da..000000000 --- a/test/syscalls/linux/sysinfo.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This is a very simple sanity test to validate that the sysinfo syscall is -// supported by gvisor and returns sane values. -#include <sys/syscall.h> -#include <sys/sysinfo.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(SysinfoTest, SysinfoIsCallable) { - struct sysinfo ignored = {}; - EXPECT_THAT(syscall(SYS_sysinfo, &ignored), SyscallSucceedsWithValue(0)); -} - -TEST(SysinfoTest, EfaultProducedOnBadAddress) { - // Validate that we return EFAULT when a bad address is provided. - // specified by man 2 sysinfo - EXPECT_THAT(syscall(SYS_sysinfo, nullptr), SyscallFailsWithErrno(EFAULT)); -} - -TEST(SysinfoTest, TotalRamSaneValue) { - struct sysinfo s = {}; - EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); - EXPECT_GT(s.totalram, 0); -} - -TEST(SysinfoTest, MemunitSet) { - struct sysinfo s = {}; - EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); - EXPECT_GE(s.mem_unit, 1); -} - -TEST(SysinfoTest, UptimeSaneValue) { - struct sysinfo s = {}; - EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); - EXPECT_GE(s.uptime, 0); -} - -TEST(SysinfoTest, UptimeIncreasingValue) { - struct sysinfo s = {}; - EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); - absl::SleepFor(absl::Seconds(2)); - struct sysinfo s2 = {}; - EXPECT_THAT(sysinfo(&s2), SyscallSucceedsWithValue(0)); - EXPECT_LT(s.uptime, s2.uptime); -} - -TEST(SysinfoTest, FreeRamSaneValue) { - struct sysinfo s = {}; - EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); - EXPECT_GT(s.freeram, 0); - EXPECT_LT(s.freeram, s.totalram); -} - -TEST(SysinfoTest, NumProcsSaneValue) { - struct sysinfo s = {}; - EXPECT_THAT(sysinfo(&s), SyscallSucceedsWithValue(0)); - EXPECT_GT(s.procs, 0); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/syslog.cc b/test/syscalls/linux/syslog.cc deleted file mode 100644 index 9a7407d96..000000000 --- a/test/syscalls/linux/syslog.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/klog.h> -#include <sys/syscall.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr int SYSLOG_ACTION_READ_ALL = 3; -constexpr int SYSLOG_ACTION_SIZE_BUFFER = 10; - -int Syslog(int type, char* buf, int len) { - return syscall(__NR_syslog, type, buf, len); -} - -// Only SYSLOG_ACTION_SIZE_BUFFER and SYSLOG_ACTION_READ_ALL are implemented in -// gVisor. - -TEST(Syslog, Size) { - EXPECT_THAT(Syslog(SYSLOG_ACTION_SIZE_BUFFER, nullptr, 0), SyscallSucceeds()); -} - -TEST(Syslog, ReadAll) { - // There might not be anything to read, so we can't check the write count. - char buf[100]; - EXPECT_THAT(Syslog(SYSLOG_ACTION_READ_ALL, buf, sizeof(buf)), - SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/sysret.cc b/test/syscalls/linux/sysret.cc deleted file mode 100644 index 19ffbd85b..000000000 --- a/test/syscalls/linux/sysret.cc +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Tests to verify that the behavior of linux and gvisor matches when -// 'sysret' returns to bad (aka non-canonical) %rip or %rsp. - -#include <linux/elf.h> -#include <sys/ptrace.h> -#include <sys/user.h> - -#include "gtest/gtest.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr uint64_t kNonCanonicalRip = 0xCCCC000000000000; -constexpr uint64_t kNonCanonicalRsp = 0xFFFF000000000000; - -class SysretTest : public ::testing::Test { - protected: - struct user_regs_struct regs_; - struct iovec iov; - pid_t child_; - - void SetUp() override { - pid_t pid = fork(); - - // Child. - if (pid == 0) { - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, 0, 0) == 0); - MaybeSave(); - TEST_PCHECK(raise(SIGSTOP) == 0); - MaybeSave(); - _exit(0); - } - - // Parent. - int status; - memset(&iov, 0, sizeof(iov)); - ASSERT_THAT(pid, SyscallSucceeds()); // Might still be < 0. - ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP); - - iov.iov_base = ®s_; - iov.iov_len = sizeof(regs_); - ASSERT_THAT(ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov), - SyscallSucceeds()); - - child_ = pid; - } - - void Detach() { - ASSERT_THAT(ptrace(PTRACE_DETACH, child_, 0, 0), SyscallSucceeds()); - } - - void SetRip(uint64_t newrip) { -#if defined(__x86_64__) - regs_.rip = newrip; -#elif defined(__aarch64__) - regs_.pc = newrip; -#else -#error "Unknown architecture" -#endif - ASSERT_THAT(ptrace(PTRACE_SETREGSET, child_, NT_PRSTATUS, &iov), - SyscallSucceeds()); - } - - void SetRsp(uint64_t newrsp) { -#if defined(__x86_64__) - regs_.rsp = newrsp; -#elif defined(__aarch64__) - regs_.sp = newrsp; -#else -#error "Unknown architecture" -#endif - ASSERT_THAT(ptrace(PTRACE_SETREGSET, child_, NT_PRSTATUS, &iov), - SyscallSucceeds()); - } - - // Wait waits for the child pid and returns the exit status. - int Wait() { - int status; - while (true) { - int rval = wait4(child_, &status, 0, NULL); - if (rval < 0) { - return rval; - } - if (rval == child_) { - return status; - } - } - } -}; - -TEST_F(SysretTest, JustDetach) { - Detach(); - int status = Wait(); - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) - << "status = " << status; -} - -TEST_F(SysretTest, BadRip) { - SetRip(kNonCanonicalRip); - Detach(); - int status = Wait(); - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) - << "status = " << status; -} - -TEST_F(SysretTest, BadRsp) { - SetRsp(kNonCanonicalRsp); - Detach(); - int status = Wait(); -#if defined(__x86_64__) - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGBUS) - << "status = " << status; -#elif defined(__aarch64__) - EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) - << "status = " << status; -#else -#error "Unknown architecture" -#endif -} -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc deleted file mode 100644 index f56c50e61..000000000 --- a/test/syscalls/linux/tcp_socket.cc +++ /dev/null @@ -1,2076 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#ifdef __linux__ -#include <linux/filter.h> -#endif // __linux__ -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <unistd.h> - -#include <limits> -#include <vector> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - addr.ss_family = family; - switch (family) { - case AF_INET: - reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr = - htonl(INADDR_LOOPBACK); - break; - case AF_INET6: - reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr = - in6addr_loopback; - break; - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } - return addr; -} - -static void FillSocketBuffers(int sender, int receiver) { - // Set the FD to O_NONBLOCK. - int opts; - int orig_opts; - ASSERT_THAT(opts = fcntl(sender, F_GETFL), SyscallSucceeds()); - orig_opts = opts; - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(sender, F_SETFL, opts), SyscallSucceeds()); - - // Set TCP_NODELAY, which will cause linux to fill the receive buffer from the - // send buffer as quickly as possibly. This way we can fill up both buffers - // faster. - constexpr int tcp_nodelay_flag = 1; - ASSERT_THAT(setsockopt(sender, IPPROTO_TCP, TCP_NODELAY, &tcp_nodelay_flag, - sizeof(tcp_nodelay_flag)), - SyscallSucceeds()); - - // Set a 256KB send/receive buffer. - int buf_sz = 1 << 18; - EXPECT_THAT( - setsockopt(receiver, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)), - SyscallSucceedsWithValue(0)); - EXPECT_THAT( - setsockopt(sender, SOL_SOCKET, SO_SNDBUF, &buf_sz, sizeof(buf_sz)), - SyscallSucceedsWithValue(0)); - - // Create a large buffer that will be used for sending. - std::vector<char> buf(1 << 16); - - // Write until we receive an error. - while (RetryEINTR(send)(sender, buf.data(), buf.size(), 0) != -1) { - // Sleep to give linux a chance to move data from the send buffer to the - // receive buffer. - usleep(10000); // 10ms. - } - // The last error should have been EWOULDBLOCK. - ASSERT_EQ(errno, EWOULDBLOCK); - - // Restore the fcntl opts - ASSERT_THAT(fcntl(sender, F_SETFL, orig_opts), SyscallSucceeds()); -} - -// Fixture for tests parameterized by the address family to use (AF_INET and -// AF_INET6) when creating sockets. -class TcpSocketTest : public ::testing::TestWithParam<int> { - protected: - // Creates three sockets that will be used by test cases -- a listener, one - // that connects, and the accepted one. - void SetUp() override; - - // Closes the sockets created by SetUp(). - void TearDown() override; - - // Listening socket. - int listener_ = -1; - - // Socket connected via connect(). - int first_fd = -1; - - // Socket connected via accept(). - int second_fd = -1; - - // Initial size of the send buffer. - int sendbuf_size_ = -1; -}; - -void TcpSocketTest::SetUp() { - ASSERT_THAT(listener_ = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), - SyscallSucceeds()); - - ASSERT_THAT(first_fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), - SyscallSucceeds()); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT( - bind(listener_, reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listener_, SOMAXCONN), SyscallSucceeds()); - - // Get the address we're listening on, then connect to it. We need to do this - // because we're allowing the stack to pick a port for us. - ASSERT_THAT(getsockname(listener_, reinterpret_cast<struct sockaddr*>(&addr), - &addrlen), - SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(connect)( - first_fd, reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Get the initial send buffer size. - socklen_t optlen = sizeof(sendbuf_size_); - ASSERT_THAT( - getsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &sendbuf_size_, &optlen), - SyscallSucceeds()); - - // Accept the connection. - ASSERT_THAT(second_fd = RetryEINTR(accept)(listener_, nullptr, nullptr), - SyscallSucceeds()); -} - -void TcpSocketTest::TearDown() { - EXPECT_THAT(close(listener_), SyscallSucceeds()); - if (first_fd >= 0) { - EXPECT_THAT(close(first_fd), SyscallSucceeds()); - } - if (second_fd >= 0) { - EXPECT_THAT(close(second_fd), SyscallSucceeds()); - } -} - -TEST_P(TcpSocketTest, ConnectOnEstablishedConnection) { - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - ASSERT_THAT(connect(first_fd, reinterpret_cast<const struct sockaddr*>(&addr), - addrlen), - SyscallFailsWithErrno(EISCONN)); - ASSERT_THAT(connect(second_fd, - reinterpret_cast<const struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(EISCONN)); -} - -TEST_P(TcpSocketTest, ShutdownWriteInTimeWait) { - EXPECT_THAT(shutdown(second_fd, SHUT_WR), SyscallSucceeds()); - EXPECT_THAT(shutdown(first_fd, SHUT_RDWR), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(1)); // Wait to enter TIME_WAIT. - EXPECT_THAT(shutdown(second_fd, SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(TcpSocketTest, ShutdownWriteInFinWait1) { - EXPECT_THAT(shutdown(second_fd, SHUT_WR), SyscallSucceeds()); - EXPECT_THAT(shutdown(second_fd, SHUT_WR), SyscallSucceeds()); - absl::SleepFor(absl::Seconds(1)); // Wait to enter FIN-WAIT2. - EXPECT_THAT(shutdown(second_fd, SHUT_WR), SyscallSucceeds()); -} - -TEST_P(TcpSocketTest, DataCoalesced) { - char buf[10]; - - // Write in two steps. - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, sizeof(buf) / 2), - SyscallSucceedsWithValue(sizeof(buf) / 2)); - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, sizeof(buf) / 2), - SyscallSucceedsWithValue(sizeof(buf) / 2)); - - // Allow stack to process both packets. - absl::SleepFor(absl::Seconds(1)); - - // Read in one shot. - EXPECT_THAT(RetryEINTR(recv)(second_fd, buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST_P(TcpSocketTest, SenderAddressIgnored) { - char buf[3]; - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - - ASSERT_THAT( - RetryEINTR(recvfrom)(second_fd, buf, sizeof(buf), 0, - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceedsWithValue(3)); - - // Check that addr remains zeroed-out. - const char* ptr = reinterpret_cast<char*>(&addr); - for (size_t i = 0; i < sizeof(addr); i++) { - EXPECT_EQ(ptr[i], 0); - } -} - -TEST_P(TcpSocketTest, SenderAddressIgnoredOnPeek) { - char buf[3]; - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - - ASSERT_THAT( - RetryEINTR(recvfrom)(second_fd, buf, sizeof(buf), MSG_PEEK, - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceedsWithValue(3)); - - // Check that addr remains zeroed-out. - const char* ptr = reinterpret_cast<char*>(&addr); - for (size_t i = 0; i < sizeof(addr); i++) { - EXPECT_EQ(ptr[i], 0); - } -} - -TEST_P(TcpSocketTest, SendtoAddressIgnored) { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - addr.ss_family = GetParam(); // FIXME(b/63803955) - - char data = '\0'; - EXPECT_THAT( - RetryEINTR(sendto)(first_fd, &data, sizeof(data), 0, - reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), - SyscallSucceedsWithValue(1)); -} - -TEST_P(TcpSocketTest, WritevZeroIovec) { - // 2 bytes just to be safe and have vecs[1] not point to something random - // (even though length is 0). - char buf[2]; - char recv_buf[1]; - - // Construct a vec where the final vector is of length 0. - iovec vecs[2] = {}; - vecs[0].iov_base = buf; - vecs[0].iov_len = 1; - vecs[1].iov_base = buf + 1; - vecs[1].iov_len = 0; - - EXPECT_THAT(RetryEINTR(writev)(first_fd, vecs, 2), - SyscallSucceedsWithValue(1)); - - EXPECT_THAT(RetryEINTR(recv)(second_fd, recv_buf, 1, 0), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(memcmp(recv_buf, buf, 1), 0); -} - -TEST_P(TcpSocketTest, ZeroWriteAllowed) { - char buf[3]; - // Send a zero length packet. - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, 0), SyscallSucceedsWithValue(0)); - // Verify that there is no packet available. - EXPECT_THAT(RetryEINTR(recv)(second_fd, buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); -} - -// Test that a non-blocking write with a buffer that is larger than the send -// buffer size will not actually write the whole thing at once. Regression test -// for b/64438887. -TEST_P(TcpSocketTest, NonblockingLargeWrite) { - // Set the FD to O_NONBLOCK. - int opts; - ASSERT_THAT(opts = fcntl(first_fd, F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(first_fd, F_SETFL, opts), SyscallSucceeds()); - - // Allocate a buffer three times the size of the send buffer. We do this with - // a vector to avoid allocating on the stack. - int size = 3 * sendbuf_size_; - std::vector<char> buf(size); - - // Try to write the whole thing. - int n; - ASSERT_THAT(n = RetryEINTR(write)(first_fd, buf.data(), size), - SyscallSucceeds()); - - // We should have written something, but not the whole thing. - EXPECT_GT(n, 0); - EXPECT_LT(n, size); -} - -// Test that a blocking write with a buffer that is larger than the send buffer -// will block until the entire buffer is sent. -TEST_P(TcpSocketTest, BlockingLargeWrite_NoRandomSave) { - // Allocate a buffer three times the size of the send buffer on the heap. We - // do this as a vector to avoid allocating on the stack. - int size = 3 * sendbuf_size_; - std::vector<char> writebuf(size); - - // Start reading the response in a loop. - int read_bytes = 0; - ScopedThread t([this, &read_bytes]() { - // Avoid interrupting the blocking write in main thread. - const DisableSave disable_save; - - // Take ownership of the FD so that we close it on failure. This will - // unblock the blocking write below. - FileDescriptor fd(second_fd); - second_fd = -1; - - char readbuf[2500] = {}; - int n = -1; - while (n != 0) { - ASSERT_THAT(n = RetryEINTR(read)(fd.get(), &readbuf, sizeof(readbuf)), - SyscallSucceeds()); - read_bytes += n; - } - }); - - // Try to write the whole thing. - int n; - ASSERT_THAT(n = WriteFd(first_fd, writebuf.data(), size), SyscallSucceeds()); - - // We should have written the whole thing. - EXPECT_EQ(n, size); - EXPECT_THAT(close(first_fd), SyscallSucceedsWithValue(0)); - first_fd = -1; - t.Join(); - - // We should have read the whole thing. - EXPECT_EQ(read_bytes, size); -} - -// Test that a send with MSG_DONTWAIT flag and buffer that larger than the send -// buffer size will not write the whole thing. -TEST_P(TcpSocketTest, LargeSendDontWait) { - // Allocate a buffer three times the size of the send buffer. We do this on - // with a vector to avoid allocating on the stack. - int size = 3 * sendbuf_size_; - std::vector<char> buf(size); - - // Try to write the whole thing with MSG_DONTWAIT flag, which can - // return a partial write. - int n; - ASSERT_THAT(n = RetryEINTR(send)(first_fd, buf.data(), size, MSG_DONTWAIT), - SyscallSucceeds()); - - // We should have written something, but not the whole thing. - EXPECT_GT(n, 0); - EXPECT_LT(n, size); -} - -// Test that a send on a non-blocking socket with a buffer that larger than the -// send buffer will not write the whole thing at once. -TEST_P(TcpSocketTest, NonblockingLargeSend) { - // Set the FD to O_NONBLOCK. - int opts; - ASSERT_THAT(opts = fcntl(first_fd, F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(first_fd, F_SETFL, opts), SyscallSucceeds()); - - // Allocate a buffer three times the size of the send buffer. We do this on - // with a vector to avoid allocating on the stack. - int size = 3 * sendbuf_size_; - std::vector<char> buf(size); - - // Try to write the whole thing. - int n; - ASSERT_THAT(n = RetryEINTR(send)(first_fd, buf.data(), size, 0), - SyscallSucceeds()); - - // We should have written something, but not the whole thing. - EXPECT_GT(n, 0); - EXPECT_LT(n, size); -} - -// Same test as above, but calls send instead of write. -TEST_P(TcpSocketTest, BlockingLargeSend_NoRandomSave) { - // Allocate a buffer three times the size of the send buffer. We do this on - // with a vector to avoid allocating on the stack. - int size = 3 * sendbuf_size_; - std::vector<char> writebuf(size); - - // Start reading the response in a loop. - int read_bytes = 0; - ScopedThread t([this, &read_bytes]() { - // Avoid interrupting the blocking write in main thread. - const DisableSave disable_save; - - // Take ownership of the FD so that we close it on failure. This will - // unblock the blocking write below. - FileDescriptor fd(second_fd); - second_fd = -1; - - char readbuf[2500] = {}; - int n = -1; - while (n != 0) { - ASSERT_THAT(n = RetryEINTR(read)(fd.get(), &readbuf, sizeof(readbuf)), - SyscallSucceeds()); - read_bytes += n; - } - }); - - // Try to send the whole thing. - int n; - ASSERT_THAT(n = SendFd(first_fd, writebuf.data(), size, 0), - SyscallSucceeds()); - - // We should have written the whole thing. - EXPECT_EQ(n, size); - EXPECT_THAT(close(first_fd), SyscallSucceedsWithValue(0)); - first_fd = -1; - t.Join(); - - // We should have read the whole thing. - EXPECT_EQ(read_bytes, size); -} - -// Test that polling on a socket with a full send buffer will block. -TEST_P(TcpSocketTest, PollWithFullBufferBlocks) { - FillSocketBuffers(first_fd, second_fd); - // Now polling on the FD with a timeout should return 0 corresponding to no - // FDs ready. - struct pollfd poll_fd = {first_fd, POLLOUT, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10), SyscallSucceedsWithValue(0)); -} - -TEST_P(TcpSocketTest, ClosedWriteBlockingSocket) { - FillSocketBuffers(first_fd, second_fd); - constexpr int timeout = 10; - struct timeval tv = {.tv_sec = timeout, .tv_usec = 0}; - EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - struct timespec begin; - struct timespec end; - const DisableSave disable_save; // Timing-related. - EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &begin), SyscallSucceeds()); - - ScopedThread send_thread([this]() { - char send_byte; - // Expect the send() to be blocked until receive timeout. - ASSERT_THAT(RetryEINTR(send)(first_fd, &send_byte, sizeof(send_byte), 0), - SyscallFailsWithErrno(EAGAIN)); - }); - - // Wait for the thread to be blocked on write. - absl::SleepFor(absl::Milliseconds(250)); - // Socket close does not have any effect on a blocked write. - ASSERT_THAT(close(first_fd), SyscallSucceeds()); - // Indicate to the cleanup routine that we are already closed. - first_fd = -1; - - send_thread.Join(); - - EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &end), SyscallSucceeds()); - // Check the lower bound on the timeout. Checking for an upper bound is - // fragile because Linux can overrun the timeout due to scheduling delays. - EXPECT_GT(ms_elapsed(begin, end), timeout * 1000 - 1); -} - -TEST_P(TcpSocketTest, ClosedReadBlockingSocket) { - constexpr int timeout = 10; - struct timeval tv = {.tv_sec = timeout, .tv_usec = 0}; - EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)), - SyscallSucceeds()); - - struct timespec begin; - struct timespec end; - const DisableSave disable_save; // Timing-related. - EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &begin), SyscallSucceeds()); - - ScopedThread read_thread([this]() { - char read_byte; - // Expect the read() to be blocked until receive timeout. - ASSERT_THAT(read(first_fd, &read_byte, sizeof(read_byte)), - SyscallFailsWithErrno(EAGAIN)); - }); - - // Wait for the thread to be blocked on read. - absl::SleepFor(absl::Milliseconds(250)); - // Socket close does not have any effect on a blocked read. - ASSERT_THAT(close(first_fd), SyscallSucceeds()); - // Indicate to the cleanup routine that we are already closed. - first_fd = -1; - - read_thread.Join(); - - EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &end), SyscallSucceeds()); - // Check the lower bound on the timeout. Checking for an upper bound is - // fragile because Linux can overrun the timeout due to scheduling delays. - EXPECT_GT(ms_elapsed(begin, end), timeout * 1000 - 1); -} - -TEST_P(TcpSocketTest, MsgTrunc) { - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(RetryEINTR(send)(first_fd, sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(second_fd, received_data, - sizeof(received_data) / 2, MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - - // Check that we didn't get anything. - char zeros[sizeof(received_data)] = {}; - EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); -} - -// MSG_CTRUNC is a return flag but linux allows it to be set on input flags -// without returning an error. -TEST_P(TcpSocketTest, MsgTruncWithCtrunc) { - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(RetryEINTR(send)(first_fd, sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT( - RetryEINTR(recv)(second_fd, received_data, sizeof(received_data) / 2, - MSG_TRUNC | MSG_CTRUNC), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - - // Check that we didn't get anything. - char zeros[sizeof(received_data)] = {}; - EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); -} - -// This test will verify that MSG_CTRUNC doesn't do anything when specified -// on input. -TEST_P(TcpSocketTest, MsgTruncWithCtruncOnly) { - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(RetryEINTR(send)(first_fd, sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(second_fd, received_data, - sizeof(received_data) / 2, MSG_CTRUNC), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - - // Since MSG_CTRUNC here had no affect, it should not behave like MSG_TRUNC. - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data) / 2)); -} - -TEST_P(TcpSocketTest, MsgTruncLargeSize) { - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(RetryEINTR(send)(first_fd, sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data) * 2] = {}; - ASSERT_THAT(RetryEINTR(recv)(second_fd, received_data, sizeof(received_data), - MSG_TRUNC), - SyscallSucceedsWithValue(sizeof(sent_data))); - - // Check that we didn't get anything. - char zeros[sizeof(received_data)] = {}; - EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); -} - -TEST_P(TcpSocketTest, MsgTruncPeek) { - char sent_data[512]; - RandomizeBuffer(sent_data, sizeof(sent_data)); - ASSERT_THAT(RetryEINTR(send)(first_fd, sent_data, sizeof(sent_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - char received_data[sizeof(sent_data)] = {}; - ASSERT_THAT(RetryEINTR(recv)(second_fd, received_data, - sizeof(received_data) / 2, MSG_TRUNC | MSG_PEEK), - SyscallSucceedsWithValue(sizeof(sent_data) / 2)); - - // Check that we didn't get anything. - char zeros[sizeof(received_data)] = {}; - EXPECT_EQ(0, memcmp(zeros, received_data, sizeof(received_data))); - - // Check that we can still get all of the data. - ASSERT_THAT( - RetryEINTR(recv)(second_fd, received_data, sizeof(received_data), 0), - SyscallSucceedsWithValue(sizeof(sent_data))); - EXPECT_EQ(0, memcmp(sent_data, received_data, sizeof(sent_data))); -} - -TEST_P(TcpSocketTest, NoDelayDefault) { - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(first_fd, IPPROTO_TCP, TCP_NODELAY, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -TEST_P(TcpSocketTest, SetNoDelay) { - ASSERT_THAT(setsockopt(first_fd, IPPROTO_TCP, TCP_NODELAY, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - EXPECT_THAT(getsockopt(first_fd, IPPROTO_TCP, TCP_NODELAY, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOn); - - ASSERT_THAT(setsockopt(first_fd, IPPROTO_TCP, TCP_NODELAY, &kSockOptOff, - sizeof(kSockOptOff)), - SyscallSucceeds()); - - EXPECT_THAT(getsockopt(first_fd, IPPROTO_TCP, TCP_NODELAY, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kSockOptOff); -} - -#ifndef TCP_INQ -#define TCP_INQ 36 -#endif - -TEST_P(TcpSocketTest, TcpInqSetSockOpt) { - char buf[1024]; - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - // TCP_INQ is disabled by default. - int val = -1; - socklen_t slen = sizeof(val); - EXPECT_THAT(getsockopt(second_fd, SOL_TCP, TCP_INQ, &val, &slen), - SyscallSucceedsWithValue(0)); - ASSERT_EQ(val, 0); - - // Try to set TCP_INQ. - val = 1; - EXPECT_THAT(setsockopt(second_fd, SOL_TCP, TCP_INQ, &val, sizeof(val)), - SyscallSucceedsWithValue(0)); - val = -1; - slen = sizeof(val); - EXPECT_THAT(getsockopt(second_fd, SOL_TCP, TCP_INQ, &val, &slen), - SyscallSucceedsWithValue(0)); - ASSERT_EQ(val, 1); - - // Try to unset TCP_INQ. - val = 0; - EXPECT_THAT(setsockopt(second_fd, SOL_TCP, TCP_INQ, &val, sizeof(val)), - SyscallSucceedsWithValue(0)); - val = -1; - slen = sizeof(val); - EXPECT_THAT(getsockopt(second_fd, SOL_TCP, TCP_INQ, &val, &slen), - SyscallSucceedsWithValue(0)); - ASSERT_EQ(val, 0); -} - -TEST_P(TcpSocketTest, TcpInq) { - char buf[1024]; - // Write more than one TCP segment. - int size = sizeof(buf); - int kChunk = sizeof(buf) / 4; - for (int i = 0; i < size; i += kChunk) { - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, kChunk), - SyscallSucceedsWithValue(kChunk)); - } - - int val = 1; - kChunk = sizeof(buf) / 2; - EXPECT_THAT(setsockopt(second_fd, SOL_TCP, TCP_INQ, &val, sizeof(val)), - SyscallSucceedsWithValue(0)); - - // Wait when all data will be in the received queue. - while (true) { - ASSERT_THAT(ioctl(second_fd, TIOCINQ, &size), SyscallSucceeds()); - if (size == sizeof(buf)) { - break; - } - absl::SleepFor(absl::Milliseconds(10)); - } - - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(sizeof(int))); - size = sizeof(buf); - struct iovec iov; - for (int i = 0; size != 0; i += kChunk) { - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - iov.iov_base = buf; - iov.iov_len = kChunk; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - ASSERT_THAT(RetryEINTR(recvmsg)(second_fd, &msg, 0), - SyscallSucceedsWithValue(kChunk)); - size -= kChunk; - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - ASSERT_EQ(cmsg->cmsg_level, SOL_TCP); - ASSERT_EQ(cmsg->cmsg_type, TCP_INQ); - - int inq = 0; - memcpy(&inq, CMSG_DATA(cmsg), sizeof(int)); - ASSERT_EQ(inq, size); - } -} - -TEST_P(TcpSocketTest, Tiocinq) { - char buf[1024]; - size_t size = sizeof(buf); - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, size), - SyscallSucceedsWithValue(size)); - - uint32_t seed = time(nullptr); - const size_t max_chunk = size / 10; - while (size > 0) { - size_t chunk = (rand_r(&seed) % max_chunk) + 1; - ssize_t read = - RetryEINTR(recvfrom)(second_fd, buf, chunk, 0, nullptr, nullptr); - ASSERT_THAT(read, SyscallSucceeds()); - size -= read; - - int inq = 0; - ASSERT_THAT(ioctl(second_fd, TIOCINQ, &inq), SyscallSucceeds()); - ASSERT_EQ(inq, size); - } -} - -TEST_P(TcpSocketTest, TcpSCMPriority) { - char buf[1024]; - ASSERT_THAT(RetryEINTR(write)(first_fd, buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - int val = 1; - EXPECT_THAT(setsockopt(second_fd, SOL_TCP, TCP_INQ, &val, sizeof(val)), - SyscallSucceedsWithValue(0)); - EXPECT_THAT( - setsockopt(second_fd, SOL_SOCKET, SO_TIMESTAMP, &val, sizeof(val)), - SyscallSucceedsWithValue(0)); - - struct msghdr msg = {}; - std::vector<char> control( - CMSG_SPACE(sizeof(struct timeval) + CMSG_SPACE(sizeof(int)))); - struct iovec iov; - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - ASSERT_THAT(RetryEINTR(recvmsg)(second_fd, &msg, 0), - SyscallSucceedsWithValue(sizeof(buf))); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - // TODO(b/78348848): SO_TIMESTAMP isn't implemented for TCP sockets. - if (!IsRunningOnGvisor() || cmsg->cmsg_level == SOL_SOCKET) { - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SO_TIMESTAMP); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct timeval))); - - cmsg = CMSG_NXTHDR(&msg, cmsg); - ASSERT_NE(cmsg, nullptr); - } - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - ASSERT_EQ(cmsg->cmsg_level, SOL_TCP); - ASSERT_EQ(cmsg->cmsg_type, TCP_INQ); - - int inq = 0; - memcpy(&inq, CMSG_DATA(cmsg), sizeof(int)); - ASSERT_EQ(inq, 0); - - cmsg = CMSG_NXTHDR(&msg, cmsg); - ASSERT_EQ(cmsg, nullptr); -} - -TEST_P(TcpSocketTest, TimeWaitPollHUP) { - shutdown(first_fd, SHUT_RDWR); - ScopedThread t([&]() { - constexpr int kTimeout = 10000; - constexpr int16_t want_events = POLLHUP; - struct pollfd pfd = { - .fd = first_fd, - .events = want_events, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - }); - shutdown(second_fd, SHUT_RDWR); - t.Join(); - // At this point first_fd should be in TIME-WAIT and polling for POLLHUP - // should return with 1 FD. - constexpr int kTimeout = 10000; - constexpr int16_t want_events = POLLHUP; - struct pollfd pfd = { - .fd = first_fd, - .events = want_events, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); -} - -INSTANTIATE_TEST_SUITE_P(AllInetTests, TcpSocketTest, - ::testing::Values(AF_INET, AF_INET6)); - -// Fixture for tests parameterized by address family that don't want the fixture -// to do things. -using SimpleTcpSocketTest = ::testing::TestWithParam<int>; - -TEST_P(SimpleTcpSocketTest, SendUnconnected) { - int fd; - ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), - SyscallSucceeds()); - FileDescriptor sock_fd(fd); - - char data = '\0'; - EXPECT_THAT(RetryEINTR(send)(fd, &data, sizeof(data), 0), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(SimpleTcpSocketTest, SendtoWithoutAddressUnconnected) { - int fd; - ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), - SyscallSucceeds()); - FileDescriptor sock_fd(fd); - - char data = '\0'; - EXPECT_THAT(RetryEINTR(sendto)(fd, &data, sizeof(data), 0, nullptr, 0), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(SimpleTcpSocketTest, SendtoWithAddressUnconnected) { - int fd; - ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), - SyscallSucceeds()); - FileDescriptor sock_fd(fd); - - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - char data = '\0'; - EXPECT_THAT( - RetryEINTR(sendto)(fd, &data, sizeof(data), 0, - reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(EPIPE)); -} - -TEST_P(SimpleTcpSocketTest, GetPeerNameUnconnected) { - int fd; - ASSERT_THAT(fd = socket(GetParam(), SOCK_STREAM, IPPROTO_TCP), - SyscallSucceeds()); - FileDescriptor sock_fd(fd); - - sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT(getpeername(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(TcpSocketTest, FullBuffer) { - // Set both FDs to be blocking. - int flags = 0; - ASSERT_THAT(flags = fcntl(first_fd, F_GETFL), SyscallSucceeds()); - EXPECT_THAT(fcntl(first_fd, F_SETFL, flags & ~O_NONBLOCK), SyscallSucceeds()); - flags = 0; - ASSERT_THAT(flags = fcntl(second_fd, F_GETFL), SyscallSucceeds()); - EXPECT_THAT(fcntl(second_fd, F_SETFL, flags & ~O_NONBLOCK), - SyscallSucceeds()); - - // 2500 was chosen as a small value that can be set on Linux. - int set_snd = 2500; - EXPECT_THAT( - setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &set_snd, sizeof(set_snd)), - SyscallSucceedsWithValue(0)); - int get_snd = -1; - socklen_t get_snd_len = sizeof(get_snd); - EXPECT_THAT( - getsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &get_snd, &get_snd_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_snd_len, sizeof(get_snd)); - EXPECT_GT(get_snd, 0); - - // 2500 was chosen as a small value that can be set on Linux and gVisor. - int set_rcv = 2500; - EXPECT_THAT( - setsockopt(second_fd, SOL_SOCKET, SO_RCVBUF, &set_rcv, sizeof(set_rcv)), - SyscallSucceedsWithValue(0)); - int get_rcv = -1; - socklen_t get_rcv_len = sizeof(get_rcv); - EXPECT_THAT( - getsockopt(second_fd, SOL_SOCKET, SO_RCVBUF, &get_rcv, &get_rcv_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_rcv_len, sizeof(get_rcv)); - EXPECT_GE(get_rcv, 2500); - - // Quick sanity test. - EXPECT_LT(get_snd + get_rcv, 2500 * IOV_MAX); - - char data[2500] = {}; - std::vector<struct iovec> iovecs; - for (int i = 0; i < IOV_MAX; i++) { - struct iovec iov = {}; - iov.iov_base = data; - iov.iov_len = sizeof(data); - iovecs.push_back(iov); - } - ScopedThread t([this, &iovecs]() { - int result = -1; - EXPECT_THAT( - result = RetryEINTR(writev)(first_fd, iovecs.data(), iovecs.size()), - SyscallSucceeds()); - EXPECT_GT(result, 1); - EXPECT_LT(result, sizeof(data) * iovecs.size()); - }); - - char recv = 0; - EXPECT_THAT(RetryEINTR(read)(second_fd, &recv, 1), - SyscallSucceedsWithValue(1)); - EXPECT_THAT(close(second_fd), SyscallSucceedsWithValue(0)); - second_fd = -1; -} - -TEST_P(TcpSocketTest, PollAfterShutdown) { - ScopedThread client_thread([this]() { - EXPECT_THAT(shutdown(first_fd, SHUT_WR), SyscallSucceedsWithValue(0)); - struct pollfd poll_fd = {first_fd, POLLIN | POLLERR | POLLHUP, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), - SyscallSucceedsWithValue(1)); - }); - - EXPECT_THAT(shutdown(second_fd, SHUT_WR), SyscallSucceedsWithValue(0)); - struct pollfd poll_fd = {second_fd, POLLIN | POLLERR | POLLHUP, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), - SyscallSucceedsWithValue(1)); -} - -TEST_P(SimpleTcpSocketTest, NonBlockingConnectRetry) { - const FileDescriptor listener = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - // Bind to some port but don't listen yet. - ASSERT_THAT( - bind(listener.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Get the address we're bound to, then connect to it. We need to do this - // because we're allowing the stack to pick a port for us. - ASSERT_THAT(getsockname(listener.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - FileDescriptor connector = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Verify that connect fails. - ASSERT_THAT( - RetryEINTR(connect)(connector.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(ECONNREFUSED)); - - // Now start listening - ASSERT_THAT(listen(listener.get(), SOMAXCONN), SyscallSucceeds()); - - // TODO(gvisor.dev/issue/3828): Issuing connect() again on a socket that - // failed first connect should succeed. - if (IsRunningOnGvisor()) { - ASSERT_THAT( - RetryEINTR(connect)(connector.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(ECONNABORTED)); - return; - } - - // Verify that connect now succeeds. - ASSERT_THAT( - RetryEINTR(connect)(connector.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Accept the connection. - const FileDescriptor accepted = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listener.get(), nullptr, nullptr)); -} - -// nonBlockingConnectNoListener returns a socket on which a connect that is -// expected to fail has been issued. -PosixErrorOr<FileDescriptor> nonBlockingConnectNoListener(const int family, - sockaddr_storage addr, - socklen_t addrlen) { - // We will first create a socket and bind to ensure we bind a port but will - // not call listen on this socket. - // Then we will create a new socket that will connect to the port bound by - // the first socket and that shoud fail. - constexpr int sock_type = SOCK_STREAM | SOCK_NONBLOCK; - int b_sock; - RETURN_ERROR_IF_SYSCALL_FAIL(b_sock = socket(family, sock_type, IPPROTO_TCP)); - FileDescriptor b(b_sock); - EXPECT_THAT(bind(b.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Get the address bound by the listening socket. - EXPECT_THAT( - getsockname(b.get(), reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - // Now create another socket and issue a connect on this one. This connect - // should fail as there is no listener. - int c_sock; - RETURN_ERROR_IF_SYSCALL_FAIL(c_sock = socket(family, sock_type, IPPROTO_TCP)); - FileDescriptor s(c_sock); - - // Now connect to the bound address and this should fail as nothing - // is listening on the bound address. - EXPECT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(EINPROGRESS)); - - // Wait for the connect to fail. - struct pollfd poll_fd = {s.get(), POLLERR, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 1000), SyscallSucceedsWithValue(1)); - return std::move(s); -} - -TEST_P(SimpleTcpSocketTest, NonBlockingConnectNoListener) { - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - const FileDescriptor s = - nonBlockingConnectNoListener(GetParam(), addr, addrlen).ValueOrDie(); - - int err; - socklen_t optlen = sizeof(err); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), - SyscallSucceeds()); - ASSERT_THAT(optlen, sizeof(err)); - EXPECT_EQ(err, ECONNREFUSED); - - unsigned char c; - ASSERT_THAT(read(s.get(), &c, sizeof(c)), SyscallSucceedsWithValue(0)); - int opts; - EXPECT_THAT(opts = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - opts &= ~O_NONBLOCK; - EXPECT_THAT(fcntl(s.get(), F_SETFL, opts), SyscallSucceeds()); - // Try connecting again. - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(ECONNABORTED)); -} - -TEST_P(SimpleTcpSocketTest, NonBlockingConnectNoListenerRead) { - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - const FileDescriptor s = - nonBlockingConnectNoListener(GetParam(), addr, addrlen).ValueOrDie(); - - unsigned char c; - ASSERT_THAT(read(s.get(), &c, 1), SyscallFailsWithErrno(ECONNREFUSED)); - ASSERT_THAT(read(s.get(), &c, 1), SyscallSucceedsWithValue(0)); - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(ECONNABORTED)); -} - -TEST_P(SimpleTcpSocketTest, NonBlockingConnectNoListenerPeek) { - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - const FileDescriptor s = - nonBlockingConnectNoListener(GetParam(), addr, addrlen).ValueOrDie(); - - unsigned char c; - ASSERT_THAT(recv(s.get(), &c, 1, MSG_PEEK), - SyscallFailsWithErrno(ECONNREFUSED)); - ASSERT_THAT(recv(s.get(), &c, 1, MSG_PEEK), SyscallSucceedsWithValue(0)); - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(ECONNABORTED)); -} - -TEST_P(SimpleTcpSocketTest, SelfConnectSendRecv_NoRandomSave) { - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - const FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT( - (bind)(s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - // Get the bound port. - ASSERT_THAT( - getsockname(s.get(), reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - constexpr int kBufSz = 1 << 20; // 1 MiB - std::vector<char> writebuf(kBufSz); - - // Start reading the response in a loop. - int read_bytes = 0; - ScopedThread t([&s, &read_bytes]() { - // Too many syscalls. - const DisableSave disable_save; - - char readbuf[2500] = {}; - int n = -1; - while (n != 0) { - ASSERT_THAT(n = RetryEINTR(read)(s.get(), &readbuf, sizeof(readbuf)), - SyscallSucceeds()); - read_bytes += n; - } - }); - - // Try to send the whole thing. - int n; - ASSERT_THAT(n = SendFd(s.get(), writebuf.data(), kBufSz, 0), - SyscallSucceeds()); - - // We should have written the whole thing. - EXPECT_EQ(n, kBufSz); - EXPECT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceedsWithValue(0)); - t.Join(); - - // We should have read the whole thing. - EXPECT_EQ(read_bytes, kBufSz); -} - -TEST_P(SimpleTcpSocketTest, SelfConnectSend_NoRandomSave) { - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - const FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - constexpr int max_seg = 256; - ASSERT_THAT( - setsockopt(s.get(), SOL_TCP, TCP_MAXSEG, &max_seg, sizeof(max_seg)), - SyscallSucceeds()); - - ASSERT_THAT(bind(s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - // Get the bound port. - ASSERT_THAT( - getsockname(s.get(), reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - std::vector<char> writebuf(512 << 10); // 512 KiB. - - // Try to send the whole thing. - int n; - ASSERT_THAT(n = SendFd(s.get(), writebuf.data(), writebuf.size(), 0), - SyscallSucceeds()); - - // We should have written the whole thing. - EXPECT_EQ(n, writebuf.size()); - EXPECT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceedsWithValue(0)); -} - -TEST_P(SimpleTcpSocketTest, NonBlockingConnect) { - const FileDescriptor listener = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT( - bind(listener.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listener.get(), SOMAXCONN), SyscallSucceeds()); - - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Set the FD to O_NONBLOCK. - int opts; - ASSERT_THAT(opts = fcntl(s.get(), F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(s.get(), F_SETFL, opts), SyscallSucceeds()); - - ASSERT_THAT(getsockname(listener.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(EINPROGRESS)); - - int t; - ASSERT_THAT(t = RetryEINTR(accept)(listener.get(), nullptr, nullptr), - SyscallSucceeds()); - - // Now polling on the FD with a timeout should return 0 corresponding to no - // FDs ready. - struct pollfd poll_fd = {s.get(), POLLOUT, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), - SyscallSucceedsWithValue(1)); - - int err; - socklen_t optlen = sizeof(err); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), - SyscallSucceeds()); - - EXPECT_EQ(err, 0); - - EXPECT_THAT(close(t), SyscallSucceeds()); -} - -TEST_P(SimpleTcpSocketTest, NonBlockingConnectRemoteClose) { - const FileDescriptor listener = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT( - bind(listener.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listener.get(), SOMAXCONN), SyscallSucceeds()); - - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(GetParam(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - - ASSERT_THAT(getsockname(listener.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(EINPROGRESS)); - - int t; - ASSERT_THAT(t = RetryEINTR(accept)(listener.get(), nullptr, nullptr), - SyscallSucceeds()); - - EXPECT_THAT(close(t), SyscallSucceeds()); - - // Now polling on the FD with a timeout should return 0 corresponding to no - // FDs ready. - struct pollfd poll_fd = {s.get(), POLLOUT, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000), - SyscallSucceedsWithValue(1)); - - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(EISCONN)); -} - -// Test that we get an ECONNREFUSED with a blocking socket when no one is -// listening on the other end. -TEST_P(SimpleTcpSocketTest, BlockingConnectRefused) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(ECONNREFUSED)); - - // Avoiding triggering save in destructor of s. - EXPECT_THAT(close(s.release()), SyscallSucceeds()); -} - -// Test that connecting to a non-listening port and thus receiving a RST is -// handled appropriately by the socket - the port that the socket was bound to -// is released and the expected error is returned. -TEST_P(SimpleTcpSocketTest, CleanupOnConnectionRefused) { - // Create a socket that is known to not be listening. As is it bound but not - // listening, when another socket connects to the port, it will refuse.. - FileDescriptor bound_s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - sockaddr_storage bound_addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t bound_addrlen = sizeof(bound_addr); - - ASSERT_THAT( - bind(bound_s.get(), reinterpret_cast<struct sockaddr*>(&bound_addr), - bound_addrlen), - SyscallSucceeds()); - - // Get the addresses the socket is bound to because the port is chosen by the - // stack. - ASSERT_THAT(getsockname(bound_s.get(), - reinterpret_cast<struct sockaddr*>(&bound_addr), - &bound_addrlen), - SyscallSucceeds()); - - // Create, initialize, and bind the socket that is used to test connecting to - // the non-listening port. - FileDescriptor client_s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - // Initialize client address to the loopback one. - sockaddr_storage client_addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t client_addrlen = sizeof(client_addr); - - ASSERT_THAT( - bind(client_s.get(), reinterpret_cast<struct sockaddr*>(&client_addr), - client_addrlen), - SyscallSucceeds()); - - ASSERT_THAT(getsockname(client_s.get(), - reinterpret_cast<struct sockaddr*>(&client_addr), - &client_addrlen), - SyscallSucceeds()); - - // Now the test: connect to the bound but not listening socket with the - // client socket. The bound socket should return a RST and cause the client - // socket to return an error and clean itself up immediately. - // The error being ECONNREFUSED diverges with RFC 793, page 37, but does what - // Linux does. - ASSERT_THAT(connect(client_s.get(), - reinterpret_cast<const struct sockaddr*>(&bound_addr), - bound_addrlen), - SyscallFailsWithErrno(ECONNREFUSED)); - - FileDescriptor new_s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Test binding to the address from the client socket. This should be okay - // if it was dropped correctly. - ASSERT_THAT( - bind(new_s.get(), reinterpret_cast<struct sockaddr*>(&client_addr), - client_addrlen), - SyscallSucceeds()); - - // Attempt #2, with the new socket and reused addr our connect should fail in - // the same way as before, not with an EADDRINUSE. - // - // TODO(gvisor.dev/issue/3828): 2nd connect on a socket which failed connect - // first time should succeed. - // gVisor never issues the second connect and returns ECONNABORTED instead. - // Linux actually sends a SYN again and gets a RST and correctly returns - // ECONNREFUSED. - if (IsRunningOnGvisor()) { - ASSERT_THAT(connect(client_s.get(), - reinterpret_cast<const struct sockaddr*>(&bound_addr), - bound_addrlen), - SyscallFailsWithErrno(ECONNABORTED)); - return; - } - ASSERT_THAT(connect(client_s.get(), - reinterpret_cast<const struct sockaddr*>(&bound_addr), - bound_addrlen), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -// Test that we get an ECONNREFUSED with a nonblocking socket. -TEST_P(SimpleTcpSocketTest, NonBlockingConnectRefused) { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(GetParam(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - ASSERT_THAT(RetryEINTR(connect)( - s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallFailsWithErrno(EINPROGRESS)); - - // We don't need to specify any events to get POLLHUP or POLLERR as these - // are added before the poll. - struct pollfd poll_fd = {s.get(), /*events=*/0, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 1000), SyscallSucceedsWithValue(1)); - - // The ECONNREFUSED should cause us to be woken up with POLLHUP. - EXPECT_NE(poll_fd.revents & (POLLHUP | POLLERR), 0); - - // Avoiding triggering save in destructor of s. - EXPECT_THAT(close(s.release()), SyscallSucceeds()); -} - -// Test that setting a supported congestion control algorithm succeeds for an -// unconnected TCP socket -TEST_P(SimpleTcpSocketTest, SetCongestionControlSucceedsForSupported) { - // This is Linux's net/tcp.h TCP_CA_NAME_MAX. - const int kTcpCaNameMax = 16; - - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - { - const char kSetCC[kTcpCaNameMax] = "reno"; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &kSetCC, - strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[kTcpCaNameMax]; - memset(got_cc, '1', sizeof(got_cc)); - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - // We ignore optlen here as the linux kernel sets optlen to the lower of the - // size of the buffer passed in or kTcpCaNameMax and not the length of the - // congestion control algorithm's actual name. - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(kTcpCaNameMax))); - } - { - const char kSetCC[kTcpCaNameMax] = "cubic"; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &kSetCC, - strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[kTcpCaNameMax]; - memset(got_cc, '1', sizeof(got_cc)); - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - // We ignore optlen here as the linux kernel sets optlen to the lower of the - // size of the buffer passed in or kTcpCaNameMax and not the length of the - // congestion control algorithm's actual name. - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(kTcpCaNameMax))); - } -} - -// This test verifies that a getsockopt(...TCP_CONGESTION) behaviour is -// consistent between linux and gvisor when the passed in buffer is smaller than -// kTcpCaNameMax. -TEST_P(SimpleTcpSocketTest, SetGetTCPCongestionShortReadBuffer) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - { - // Verify that getsockopt/setsockopt work with buffers smaller than - // kTcpCaNameMax. - const char kSetCC[] = "cubic"; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &kSetCC, - strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[sizeof(kSetCC)]; - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(sizeof(got_cc), optlen); - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(got_cc))); - } -} - -// This test verifies that a getsockopt(...TCP_CONGESTION) behaviour is -// consistent between linux and gvisor when the passed in buffer is larger than -// kTcpCaNameMax. -TEST_P(SimpleTcpSocketTest, SetGetTCPCongestionLargeReadBuffer) { - // This is Linux's net/tcp.h TCP_CA_NAME_MAX. - const int kTcpCaNameMax = 16; - - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - { - // Verify that getsockopt works with buffers larger than - // kTcpCaNameMax. - const char kSetCC[] = "cubic"; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &kSetCC, - strlen(kSetCC)), - SyscallSucceedsWithValue(0)); - - char got_cc[kTcpCaNameMax + 5]; - socklen_t optlen = sizeof(got_cc); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - // Linux copies the minimum of kTcpCaNameMax or the length of the passed in - // buffer and sets optlen to the number of bytes actually copied - // irrespective of the actual length of the congestion control name. - EXPECT_EQ(kTcpCaNameMax, optlen); - EXPECT_EQ(0, memcmp(got_cc, kSetCC, sizeof(kSetCC))); - } -} - -// Test that setting an unsupported congestion control algorithm fails for an -// unconnected TCP socket. -TEST_P(SimpleTcpSocketTest, SetCongestionControlFailsForUnsupported) { - // This is Linux's net/tcp.h TCP_CA_NAME_MAX. - const int kTcpCaNameMax = 16; - - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - char old_cc[kTcpCaNameMax]; - socklen_t optlen = sizeof(old_cc); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &old_cc, &optlen), - SyscallSucceedsWithValue(0)); - - const char kSetCC[] = "invalid_ca_kSetCC"; - ASSERT_THAT( - setsockopt(s.get(), SOL_TCP, TCP_CONGESTION, &kSetCC, strlen(kSetCC)), - SyscallFailsWithErrno(ENOENT)); - - char got_cc[kTcpCaNameMax]; - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_CONGESTION, &got_cc, &optlen), - SyscallSucceedsWithValue(0)); - // We ignore optlen here as the linux kernel sets optlen to the lower of the - // size of the buffer passed in or kTcpCaNameMax and not the length of the - // congestion control algorithm's actual name. - EXPECT_EQ(0, memcmp(got_cc, old_cc, sizeof(kTcpCaNameMax))); -} - -TEST_P(SimpleTcpSocketTest, MaxSegDefault) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - constexpr int kDefaultMSS = 536; - int tcp_max_seg; - socklen_t optlen = sizeof(tcp_max_seg); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_MAXSEG, &tcp_max_seg, &optlen), - SyscallSucceedsWithValue(0)); - - EXPECT_EQ(kDefaultMSS, tcp_max_seg); - EXPECT_EQ(sizeof(tcp_max_seg), optlen); -} - -TEST_P(SimpleTcpSocketTest, SetMaxSeg) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - constexpr int kDefaultMSS = 536; - constexpr int kTCPMaxSeg = 1024; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_MAXSEG, &kTCPMaxSeg, - sizeof(kTCPMaxSeg)), - SyscallSucceedsWithValue(0)); - - // Linux actually never returns the user_mss value. It will always return the - // default MSS value defined above for an unconnected socket and always return - // the actual current MSS for a connected one. - int optval; - socklen_t optlen = sizeof(optval); - ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_MAXSEG, &optval, &optlen), - SyscallSucceedsWithValue(0)); - - EXPECT_EQ(kDefaultMSS, optval); - EXPECT_EQ(sizeof(optval), optlen); -} - -TEST_P(SimpleTcpSocketTest, SetMaxSegFailsForInvalidMSSValues) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - { - constexpr int tcp_max_seg = 10; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_MAXSEG, &tcp_max_seg, - sizeof(tcp_max_seg)), - SyscallFailsWithErrno(EINVAL)); - } - { - constexpr int tcp_max_seg = 75000; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_MAXSEG, &tcp_max_seg, - sizeof(tcp_max_seg)), - SyscallFailsWithErrno(EINVAL)); - } -} - -TEST_P(SimpleTcpSocketTest, SetTCPUserTimeout) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - { - constexpr int kTCPUserTimeout = -1; - EXPECT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &kTCPUserTimeout, sizeof(kTCPUserTimeout)), - SyscallFailsWithErrno(EINVAL)); - } - - // kTCPUserTimeout is in milliseconds. - constexpr int kTCPUserTimeout = 100; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, - &kTCPUserTimeout, sizeof(kTCPUserTimeout)), - SyscallSucceedsWithValue(0)); - 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, 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_DEFER_ACCEPT, &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_DEFER_ACCEPT, &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); -} - -TEST_P(SimpleTcpSocketTest, RecvOnClosedSocket) { - auto s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - char buf[1]; - EXPECT_THAT(recv(s.get(), buf, 0, 0), SyscallFailsWithErrno(ENOTCONN)); - EXPECT_THAT(recv(s.get(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(SimpleTcpSocketTest, TCPConnectSoRcvBufRace) { - auto s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(GetParam(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - RetryEINTR(connect)(s.get(), reinterpret_cast<struct sockaddr*>(&addr), - addrlen); - int buf_sz = 1 << 18; - EXPECT_THAT( - setsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)), - SyscallSucceedsWithValue(0)); -} - -TEST_P(SimpleTcpSocketTest, SetTCPSynCntLessThanOne) { - 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_SYNCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - int default_syn_cnt = get; - - { - // TCP_SYNCNT less than 1 should be rejected with an EINVAL. - constexpr int kZero = 0; - EXPECT_THAT( - setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kZero, sizeof(kZero)), - SyscallFailsWithErrno(EINVAL)); - - // TCP_SYNCNT less than 1 should be rejected with an EINVAL. - constexpr int kNeg = -1; - EXPECT_THAT( - setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kNeg, sizeof(kNeg)), - SyscallFailsWithErrno(EINVAL)); - - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(default_syn_cnt, get); - } -} - -TEST_P(SimpleTcpSocketTest, GetTCPSynCntDefault) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - int get = -1; - socklen_t get_len = sizeof(get); - constexpr int kDefaultSynCnt = 6; - - ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kDefaultSynCnt); -} - -TEST_P(SimpleTcpSocketTest, SetTCPSynCntGreaterThanOne) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - constexpr int kTCPSynCnt = 20; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kTCPSynCnt, - sizeof(kTCPSynCnt)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kTCPSynCnt); -} - -TEST_P(SimpleTcpSocketTest, SetTCPSynCntAboveMax) { - 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_SYNCNT, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - int default_syn_cnt = get; - { - constexpr int kTCPSynCnt = 256; - ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &kTCPSynCnt, - sizeof(kTCPSynCnt)), - SyscallFailsWithErrno(EINVAL)); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(s.get(), IPPROTO_TCP, TCP_SYNCNT, &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, default_syn_cnt); - } -} - -TEST_P(SimpleTcpSocketTest, SetTCPWindowClampBelowMinRcvBuf) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Discover minimum receive buf by setting a really low value - // for the receive buffer. - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &kZero, sizeof(kZero)), - SyscallSucceeds()); - - // Now retrieve the minimum value for SO_RCVBUF as the set above should - // have caused SO_RCVBUF for the socket to be set to the minimum. - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - int min_so_rcvbuf = get; - - { - // TCP_WINDOW_CLAMP less than min_so_rcvbuf/2 should be set to - // min_so_rcvbuf/2. - int below_half_min_rcvbuf = min_so_rcvbuf / 2 - 1; - EXPECT_THAT( - setsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, - &below_half_min_rcvbuf, sizeof(below_half_min_rcvbuf)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(min_so_rcvbuf / 2, get); - } -} - -TEST_P(SimpleTcpSocketTest, SetTCPWindowClampZeroClosedSocket) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - constexpr int kZero = 0; - ASSERT_THAT( - setsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &kZero, sizeof(kZero)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &get, &get_len), - SyscallSucceeds()); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(get, kZero); -} - -TEST_P(SimpleTcpSocketTest, SetTCPWindowClampAboveHalfMinRcvBuf) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Discover minimum receive buf by setting a really low value - // for the receive buffer. - constexpr int kZero = 0; - EXPECT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &kZero, sizeof(kZero)), - SyscallSucceeds()); - - // Now retrieve the minimum value for SO_RCVBUF as the set above should - // have caused SO_RCVBUF for the socket to be set to the minimum. - int get = -1; - socklen_t get_len = sizeof(get); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_RCVBUF, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - int min_so_rcvbuf = get; - - { - int above_half_min_rcv_buf = min_so_rcvbuf / 2 + 1; - EXPECT_THAT( - setsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, - &above_half_min_rcv_buf, sizeof(above_half_min_rcv_buf)), - SyscallSucceeds()); - - int get = -1; - socklen_t get_len = sizeof(get); - - ASSERT_THAT( - getsockopt(s.get(), IPPROTO_TCP, TCP_WINDOW_CLAMP, &get, &get_len), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(get_len, sizeof(get)); - EXPECT_EQ(above_half_min_rcv_buf, get); - } -} - -#ifdef __linux__ - -// TODO(gvisor.dev/2746): Support SO_ATTACH_FILTER/SO_DETACH_FILTER. -// gVisor currently silently ignores attaching a filter. -TEST_P(SimpleTcpSocketTest, SetSocketAttachDetachFilter) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - // Program generated using sudo tcpdump -i lo tcp and port 1234 -dd - struct sock_filter code[] = { - {0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000086dd}, - {0x30, 0, 0, 0x00000014}, {0x15, 0, 15, 0x00000006}, - {0x28, 0, 0, 0x00000036}, {0x15, 12, 0, 0x000004d2}, - {0x28, 0, 0, 0x00000038}, {0x15, 10, 11, 0x000004d2}, - {0x15, 0, 10, 0x00000800}, {0x30, 0, 0, 0x00000017}, - {0x15, 0, 8, 0x00000006}, {0x28, 0, 0, 0x00000014}, - {0x45, 6, 0, 0x00001fff}, {0xb1, 0, 0, 0x0000000e}, - {0x48, 0, 0, 0x0000000e}, {0x15, 2, 0, 0x000004d2}, - {0x48, 0, 0, 0x00000010}, {0x15, 0, 1, 0x000004d2}, - {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}, - }; - struct sock_fprog bpf = { - .len = ABSL_ARRAYSIZE(code), - .filter = code, - }; - ASSERT_THAT( - setsockopt(s.get(), SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)), - SyscallSucceeds()); - - constexpr int val = 0; - ASSERT_THAT( - setsockopt(s.get(), SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallSucceeds()); -} - -#endif // __linux__ - -TEST_P(SimpleTcpSocketTest, SetSocketDetachFilterNoInstalledFilter) { - // TODO(gvisor.dev/2746): Support SO_ATTACH_FILTER/SO_DETACH_FILTER. - SKIP_IF(IsRunningOnGvisor()); - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - constexpr int val = 0; - ASSERT_THAT( - setsockopt(s.get(), SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallFailsWithErrno(ENOENT)); -} - -TEST_P(SimpleTcpSocketTest, GetSocketDetachFilter) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_DETACH_FILTER, &val, &val_len), - SyscallFailsWithErrno(ENOPROTOOPT)); -} - -TEST_P(SimpleTcpSocketTest, CloseNonConnectedLingerOption) { - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - constexpr int kLingerTimeout = 10; // Seconds. - - // Set the SO_LINGER option. - struct linger sl = { - .l_onoff = 1, - .l_linger = kLingerTimeout, - }; - ASSERT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)), - SyscallSucceeds()); - - struct pollfd poll_fd = { - .fd = s.get(), - .events = POLLHUP, - }; - constexpr int kPollTimeoutMs = 0; - ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs), - SyscallSucceedsWithValue(1)); - - auto const start_time = absl::Now(); - EXPECT_THAT(close(s.release()), SyscallSucceeds()); - auto const end_time = absl::Now(); - - // Close() should not linger and return immediately. - ASSERT_LT((end_time - start_time), absl::Seconds(kLingerTimeout)); -} - -// Tests that SO_ACCEPTCONN returns non zero value for listening sockets. -TEST_P(TcpSocketTest, GetSocketAcceptConnListener) { - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT(getsockopt(listener_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceeds()); - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 1); -} - -// Tests that SO_ACCEPTCONN returns zero value for not listening sockets. -TEST_P(TcpSocketTest, GetSocketAcceptConnNonListener) { - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT(getsockopt(first_fd, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceeds()); - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); - - ASSERT_THAT(getsockopt(second_fd, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceeds()); - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); -} - -TEST_P(SimpleTcpSocketTest, GetSocketAcceptConnWithShutdown) { - // TODO(b/171345701): Fix the TCP state for listening socket on shutdown. - SKIP_IF(IsRunningOnGvisor()); - - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = - ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT(bind(s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(s.get(), SOMAXCONN), SyscallSucceeds()); - - int got = -1; - socklen_t length = sizeof(got); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceeds()); - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 1); - - EXPECT_THAT(shutdown(s.get(), SHUT_RD), SyscallSucceeds()); - ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), - SyscallSucceeds()); - ASSERT_EQ(length, sizeof(got)); - EXPECT_EQ(got, 0); -} - -// Tests that connecting to an unspecified address results in ECONNREFUSED. -TEST_P(SimpleTcpSocketTest, ConnectUnspecifiedAddress) { - sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - memset(&addr, 0, addrlen); - addr.ss_family = GetParam(); - auto do_connect = [&addr, addrlen]() { - FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( - Socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP)); - ASSERT_THAT( - RetryEINTR(connect)(s.get(), reinterpret_cast<struct sockaddr*>(&addr), - addrlen), - SyscallFailsWithErrno(ECONNREFUSED)); - }; - do_connect(); - // Test the v4 mapped address as well. - if (GetParam() == AF_INET6) { - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr); - sin6->sin6_addr.s6_addr[10] = sin6->sin6_addr.s6_addr[11] = 0xff; - do_connect(); - } -} - -INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest, - ::testing::Values(AF_INET, AF_INET6)); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/tgkill.cc b/test/syscalls/linux/tgkill.cc deleted file mode 100644 index 80acae5de..000000000 --- a/test/syscalls/linux/tgkill.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(TgkillTest, InvalidTID) { - EXPECT_THAT(tgkill(getpid(), -1, 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(tgkill(getpid(), 0, 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(TgkillTest, InvalidTGID) { - EXPECT_THAT(tgkill(-1, gettid(), 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(tgkill(0, gettid(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(TgkillTest, ValidInput) { - EXPECT_THAT(tgkill(getpid(), gettid(), 0), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/time.cc b/test/syscalls/linux/time.cc deleted file mode 100644 index e75bba669..000000000 --- a/test/syscalls/linux/time.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <time.h> - -#include "gtest/gtest.h" -#include "test/util/proc_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr long kFudgeSeconds = 5; - -#if defined(__x86_64__) || defined(__i386__) -// Mimics the time(2) wrapper from glibc prior to 2.15. -time_t vsyscall_time(time_t* t) { - constexpr uint64_t kVsyscallTimeEntry = 0xffffffffff600400; - return reinterpret_cast<time_t (*)(time_t*)>(kVsyscallTimeEntry)(t); -} - -TEST(TimeTest, VsyscallTime_Succeeds) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); - - time_t t1, t2; - - { - const DisableSave ds; // Timing assertions. - EXPECT_THAT(time(&t1), SyscallSucceeds()); - EXPECT_THAT(vsyscall_time(&t2), SyscallSucceeds()); - } - - // Time should be monotonic. - EXPECT_LE(static_cast<long>(t1), static_cast<long>(t2)); - - // Check that it's within kFudge seconds. - EXPECT_LE(static_cast<long>(t2), static_cast<long>(t1) + kFudgeSeconds); - - // Redo with save. - EXPECT_THAT(time(&t1), SyscallSucceeds()); - EXPECT_THAT(vsyscall_time(&t2), SyscallSucceeds()); - - // Time should be monotonic. - EXPECT_LE(static_cast<long>(t1), static_cast<long>(t2)); -} - -TEST(TimeTest, VsyscallTime_InvalidAddressSIGSEGV) { - EXPECT_EXIT(vsyscall_time(reinterpret_cast<time_t*>(0x1)), - ::testing::KilledBySignal(SIGSEGV), ""); -} - -// Mimics the gettimeofday(2) wrapper from the Go runtime <= 1.2. -int vsyscall_gettimeofday(struct timeval* tv, struct timezone* tz) { - constexpr uint64_t kVsyscallGettimeofdayEntry = 0xffffffffff600000; - return reinterpret_cast<int (*)(struct timeval*, struct timezone*)>( - kVsyscallGettimeofdayEntry)(tv, tz); -} - -TEST(TimeTest, VsyscallGettimeofday_Succeeds) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); - - struct timeval tv1, tv2; - struct timezone tz1, tz2; - - { - const DisableSave ds; // Timing assertions. - EXPECT_THAT(gettimeofday(&tv1, &tz1), SyscallSucceeds()); - EXPECT_THAT(vsyscall_gettimeofday(&tv2, &tz2), SyscallSucceeds()); - } - - // See above. - EXPECT_LE(static_cast<long>(tv1.tv_sec), static_cast<long>(tv2.tv_sec)); - EXPECT_LE(static_cast<long>(tv2.tv_sec), - static_cast<long>(tv1.tv_sec) + kFudgeSeconds); - - // Redo with save. - EXPECT_THAT(gettimeofday(&tv1, &tz1), SyscallSucceeds()); - EXPECT_THAT(vsyscall_gettimeofday(&tv2, &tz2), SyscallSucceeds()); -} - -TEST(TimeTest, VsyscallGettimeofday_InvalidAddressSIGSEGV) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); - - EXPECT_EXIT(vsyscall_gettimeofday(reinterpret_cast<struct timeval*>(0x1), - reinterpret_cast<struct timezone*>(0x1)), - ::testing::KilledBySignal(SIGSEGV), ""); -} -#endif - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/timerfd.cc b/test/syscalls/linux/timerfd.cc deleted file mode 100644 index c4f8fdd7a..000000000 --- a/test/syscalls/linux/timerfd.cc +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <poll.h> -#include <sys/timerfd.h> -#include <time.h> - -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/file_descriptor.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Wrapper around timerfd_create(2) that returns a FileDescriptor. -PosixErrorOr<FileDescriptor> TimerfdCreate(int clockid, int flags) { - int fd = timerfd_create(clockid, flags); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "timerfd_create failed"); - } - return FileDescriptor(fd); -} - -// In tests that race a timerfd with a sleep, some slack is required because: -// -// - Timerfd expirations are asynchronous with respect to nanosleeps. -// -// - Because clock_gettime(CLOCK_MONOTONIC) is implemented through the VDSO, -// it technically uses a closely-related, but distinct, time domain from the -// CLOCK_MONOTONIC used to trigger timerfd expirations. The same applies to -// CLOCK_BOOTTIME which is an alias for CLOCK_MONOTONIC. -absl::Duration TimerSlack() { return absl::Milliseconds(500); } - -class TimerfdTest : public ::testing::TestWithParam<int> {}; - -TEST_P(TimerfdTest, IsInitiallyStopped) { - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - struct itimerspec its = {}; - ASSERT_THAT(timerfd_gettime(tfd.get(), &its), SyscallSucceeds()); - EXPECT_EQ(0, its.it_value.tv_sec); - EXPECT_EQ(0, its.it_value.tv_nsec); -} - -TEST_P(TimerfdTest, SingleShot) { - constexpr absl::Duration kDelay = absl::Seconds(1); - - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - struct itimerspec its = {}; - its.it_value = absl::ToTimespec(kDelay); - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - - // The timer should fire exactly once since the interval is zero. - absl::SleepFor(kDelay + TimerSlack()); - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallSucceedsWithValue(sizeof(uint64_t))); - EXPECT_EQ(1, val); -} - -TEST_P(TimerfdTest, Periodic) { - constexpr absl::Duration kDelay = absl::Seconds(1); - constexpr int kPeriods = 3; - - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - struct itimerspec its = {}; - its.it_value = absl::ToTimespec(kDelay); - its.it_interval = absl::ToTimespec(kDelay); - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - - // Expect to see at least kPeriods expirations. More may occur due to the - // timer slack, or due to delays from scheduling or save/restore. - absl::SleepFor(kPeriods * kDelay + TimerSlack()); - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallSucceedsWithValue(sizeof(uint64_t))); - EXPECT_GE(val, kPeriods); -} - -TEST_P(TimerfdTest, BlockingRead) { - constexpr absl::Duration kDelay = absl::Seconds(3); - - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - struct itimerspec its = {}; - its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); - auto const start_time = absl::Now(); - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - - // read should block until the timer fires. - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallSucceedsWithValue(sizeof(uint64_t))); - auto const end_time = absl::Now(); - EXPECT_EQ(1, val); - EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay); -} - -TEST_P(TimerfdTest, NonblockingRead_NoRandomSave) { - constexpr absl::Duration kDelay = absl::Seconds(5); - - auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); - - // Since the timer is initially disabled and has never fired, read should - // return EAGAIN. - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallFailsWithErrno(EAGAIN)); - - DisableSave ds; // Timing-sensitive. - - // Arm the timer. - struct itimerspec its = {}; - its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - - // Since the timer has not yet fired, read should return EAGAIN. - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallFailsWithErrno(EAGAIN)); - - ds.reset(); // No longer timing-sensitive. - - // After the timer fires, read should indicate 1 expiration. - absl::SleepFor(kDelay + TimerSlack()); - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallSucceedsWithValue(sizeof(uint64_t))); - EXPECT_EQ(1, val); - - // The successful read should have reset the number of expirations. - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) { - constexpr absl::Duration kDelay = absl::Seconds(3); - - auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); - struct itimerspec its = {}; - its.it_value.tv_sec = absl::ToInt64Seconds(kDelay); - auto const start_time = absl::Now(); - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - - // poll should block until the timer fires. - struct pollfd pfd = {}; - pfd.fd = tfd.get(); - pfd.events = POLLIN; - ASSERT_THAT(poll(&pfd, /* nfds = */ 1, - /* timeout = */ 2 * absl::ToInt64Seconds(kDelay) * 1000), - SyscallSucceedsWithValue(1)); - auto const end_time = absl::Now(); - EXPECT_EQ(POLLIN, pfd.revents); - EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay); - - // Call timerfd_settime again with a value of 0. This should reset the number - // of expirations to 0, causing read to return EAGAIN since the timerfd is - // non-blocking. - its.it_value.tv_sec = 0; - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(TimerfdTest, SetAbsoluteTime) { - constexpr absl::Duration kDelay = absl::Seconds(3); - - // Use a non-blocking timerfd so that if TFD_TIMER_ABSTIME is incorrectly - // non-functional, we get EAGAIN rather than a test timeout. - auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); - struct itimerspec its = {}; - ASSERT_THAT(clock_gettime(GetParam(), &its.it_value), SyscallSucceeds()); - its.it_value.tv_sec += absl::ToInt64Seconds(kDelay); - ASSERT_THAT(timerfd_settime(tfd.get(), TFD_TIMER_ABSTIME, &its, nullptr), - SyscallSucceeds()); - - absl::SleepFor(kDelay + TimerSlack()); - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallSucceedsWithValue(sizeof(uint64_t))); - EXPECT_EQ(1, val); -} - -TEST_P(TimerfdTest, IllegalSeek) { - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - if (!IsRunningWithVFS1()) { - EXPECT_THAT(lseek(tfd.get(), 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); - } -} - -TEST_P(TimerfdTest, IllegalPread) { - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - int val; - EXPECT_THAT(pread(tfd.get(), &val, sizeof(val), 0), - SyscallFailsWithErrno(ESPIPE)); -} - -TEST_P(TimerfdTest, IllegalPwrite) { - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0)); - EXPECT_THAT(pwrite(tfd.get(), "x", 1, 0), SyscallFailsWithErrno(ESPIPE)); - if (!IsRunningWithVFS1()) { - } -} - -TEST_P(TimerfdTest, IllegalWrite) { - auto const tfd = - ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK)); - uint64_t val = 0; - EXPECT_THAT(write(tfd.get(), &val, sizeof(val)), - SyscallFailsWithErrno(EINVAL)); -} - -std::string PrintClockId(::testing::TestParamInfo<int> info) { - switch (info.param) { - case CLOCK_MONOTONIC: - return "CLOCK_MONOTONIC"; - case CLOCK_BOOTTIME: - return "CLOCK_BOOTTIME"; - default: - return absl::StrCat(info.param); - } -} - -INSTANTIATE_TEST_SUITE_P(AllTimerTypes, TimerfdTest, - ::testing::Values(CLOCK_MONOTONIC, CLOCK_BOOTTIME), - PrintClockId); - -TEST(TimerfdClockRealtimeTest, ClockRealtime) { - // Since CLOCK_REALTIME can, by definition, change, we can't make any - // non-flaky assertions about the amount of time it takes for a - // CLOCK_REALTIME-based timer to expire. Just check that it expires at all, - // and hope it happens before the test times out. - constexpr int kDelaySecs = 1; - - auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_REALTIME, 0)); - struct itimerspec its = {}; - its.it_value.tv_sec = kDelaySecs; - ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr), - SyscallSucceeds()); - - uint64_t val = 0; - ASSERT_THAT(ReadFd(tfd.get(), &val, sizeof(uint64_t)), - SyscallSucceedsWithValue(sizeof(uint64_t))); - EXPECT_EQ(1, val); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/timers.cc b/test/syscalls/linux/timers.cc deleted file mode 100644 index 93a98adb1..000000000 --- a/test/syscalls/linux/timers.cc +++ /dev/null @@ -1,565 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <signal.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <syscall.h> -#include <time.h> -#include <unistd.h> - -#include <atomic> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/timer_util.h" - -ABSL_FLAG(bool, timers_test_sleep, false, - "If true, sleep forever instead of running tests."); - -using ::testing::_; -using ::testing::AnyOf; - -namespace gvisor { -namespace testing { -namespace { - -#ifndef CPUCLOCK_PROF -#define CPUCLOCK_PROF 0 -#endif // CPUCLOCK_PROF - -PosixErrorOr<absl::Duration> ProcessCPUTime(pid_t pid) { - // Use pid-specific CPUCLOCK_PROF, which is the clock used to enforce - // RLIMIT_CPU. - clockid_t clockid = (~static_cast<clockid_t>(pid) << 3) | CPUCLOCK_PROF; - - struct timespec ts; - int ret = clock_gettime(clockid, &ts); - if (ret < 0) { - return PosixError(errno, "clock_gettime failed"); - } - - return absl::DurationFromTimespec(ts); -} - -void NoopSignalHandler(int signo) { - TEST_CHECK_MSG(SIGXCPU == signo, - "NoopSigHandler did not receive expected signal"); -} - -void UninstallingSignalHandler(int signo) { - TEST_CHECK_MSG(SIGXCPU == signo, - "UninstallingSignalHandler did not receive expected signal"); - struct sigaction rev_action; - rev_action.sa_handler = SIG_DFL; - rev_action.sa_flags = 0; - sigemptyset(&rev_action.sa_mask); - sigaction(SIGXCPU, &rev_action, nullptr); -} - -TEST(TimerTest, ProcessKilledOnCPUSoftLimit) { - constexpr absl::Duration kSoftLimit = absl::Seconds(1); - constexpr absl::Duration kHardLimit = absl::Seconds(3); - - struct rlimit cpu_limits; - cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); - cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); - - int pid = fork(); - MaybeSave(); - if (pid == 0) { - TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); - MaybeSave(); - for (;;) { - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto c = Cleanup([pid] { - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(WTERMSIG(status), SIGXCPU); - }); - - // Wait for the child to exit, but do not reap it. This will allow us to check - // its CPU usage while it is zombied. - EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), - SyscallSucceeds()); - - // Assert that the child spent 1s of CPU before getting killed. - // - // We must be careful to use CPUCLOCK_PROF, the same clock used for RLIMIT_CPU - // enforcement, to get correct results. Note that this is slightly different - // from rusage-reported CPU usage: - // - // RLIMIT_CPU, CPUCLOCK_PROF use kernel/sched/cputime.c:thread_group_cputime. - // rusage uses kernel/sched/cputime.c:thread_group_cputime_adjusted. - absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); - EXPECT_GE(cpu, kSoftLimit); - - // Child did not make it to the hard limit. - // - // Linux sends SIGXCPU synchronously with CPU tick updates. See - // kernel/time/timer.c:update_process_times: - // => account_process_tick // update task CPU usage. - // => run_posix_cpu_timers // enforce RLIMIT_CPU, sending signal. - // - // Thus, only chance for this to flake is if the system time required to - // deliver the signal exceeds 2s. - EXPECT_LT(cpu, kHardLimit); -} - -TEST(TimerTest, ProcessPingedRepeatedlyAfterCPUSoftLimit) { - struct sigaction new_action; - new_action.sa_handler = UninstallingSignalHandler; - new_action.sa_flags = 0; - sigemptyset(&new_action.sa_mask); - - constexpr absl::Duration kSoftLimit = absl::Seconds(1); - constexpr absl::Duration kHardLimit = absl::Seconds(10); - - struct rlimit cpu_limits; - cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); - cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); - - int pid = fork(); - MaybeSave(); - if (pid == 0) { - TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); - MaybeSave(); - TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); - MaybeSave(); - for (;;) { - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto c = Cleanup([pid] { - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(WTERMSIG(status), SIGXCPU); - }); - - // Wait for the child to exit, but do not reap it. This will allow us to check - // its CPU usage while it is zombied. - EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), - SyscallSucceeds()); - - absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); - // Following signals come every CPU second. - EXPECT_GE(cpu, kSoftLimit + absl::Seconds(1)); - - // Child did not make it to the hard limit. - // - // As above, should not flake. - EXPECT_LT(cpu, kHardLimit); -} - -TEST(TimerTest, ProcessKilledOnCPUHardLimit) { - struct sigaction new_action; - new_action.sa_handler = NoopSignalHandler; - new_action.sa_flags = 0; - sigemptyset(&new_action.sa_mask); - - constexpr absl::Duration kSoftLimit = absl::Seconds(1); - constexpr absl::Duration kHardLimit = absl::Seconds(3); - - struct rlimit cpu_limits; - cpu_limits.rlim_cur = absl::ToInt64Seconds(kSoftLimit); - cpu_limits.rlim_max = absl::ToInt64Seconds(kHardLimit); - - int pid = fork(); - MaybeSave(); - if (pid == 0) { - TEST_PCHECK(sigaction(SIGXCPU, &new_action, nullptr) == 0); - MaybeSave(); - TEST_PCHECK(setrlimit(RLIMIT_CPU, &cpu_limits) == 0); - MaybeSave(); - for (;;) { - } - } - ASSERT_THAT(pid, SyscallSucceeds()); - auto c = Cleanup([pid] { - int status; - EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(WTERMSIG(status), SIGKILL); - }); - - // Wait for the child to exit, but do not reap it. This will allow us to check - // its CPU usage while it is zombied. - EXPECT_THAT(waitid(P_PID, pid, nullptr, WEXITED | WNOWAIT), - SyscallSucceeds()); - - absl::Duration cpu = ASSERT_NO_ERRNO_AND_VALUE(ProcessCPUTime(pid)); - EXPECT_GE(cpu, kHardLimit); -} - -// See timerfd.cc:TimerSlack() for rationale. -constexpr absl::Duration kTimerSlack = absl::Milliseconds(500); - -TEST(IntervalTimerTest, IsInitiallyStopped) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - const struct itimerspec its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); - EXPECT_EQ(0, its.it_value.tv_sec); - EXPECT_EQ(0, its.it_value.tv_nsec); -} - -// Kernel can create multiple timers without issue. -// -// Regression test for gvisor.dev/issue/1738. -TEST(IntervalTimerTest, MultipleTimers) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer1 = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - const auto timer2 = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); -} - -TEST(IntervalTimerTest, SingleShotSilent) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kDelay = absl::Seconds(1); - struct itimerspec its = {}; - its.it_value = absl::ToTimespec(kDelay); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // The timer should count down to 0 and stop since the interval is zero. No - // overruns should be counted. - absl::SleepFor(kDelay + kTimerSlack); - its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); - EXPECT_EQ(0, its.it_value.tv_sec); - EXPECT_EQ(0, its.it_value.tv_nsec); - EXPECT_THAT(timer.Overruns(), IsPosixErrorOkAndHolds(0)); -} - -TEST(IntervalTimerTest, PeriodicSilent) { - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_NONE; - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - absl::SleepFor(kPeriod * 3 + kTimerSlack); - - // The timer should still be running. - its = ASSERT_NO_ERRNO_AND_VALUE(timer.Get()); - EXPECT_TRUE(its.it_value.tv_nsec != 0 || its.it_value.tv_sec != 0); - - // Timer expirations are not counted as overruns under SIGEV_NONE. - EXPECT_THAT(timer.Overruns(), IsPosixErrorOkAndHolds(0)); -} - -std::atomic<int> counted_signals; - -void IntervalTimerCountingSignalHandler(int sig, siginfo_t* info, - void* ucontext) { - counted_signals.fetch_add(1 + info->si_overrun); -} - -TEST(IntervalTimerTest, PeriodicGroupDirectedSignal) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Install our signal handler. - counted_signals.store(0); - struct sigaction sa = {}; - sa.sa_sigaction = IntervalTimerCountingSignalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - const auto scoped_sigaction = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); - - // Ensure that kSigno is unblocked on at least one thread. - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, kSigno)); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_SIGNAL; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - absl::SleepFor(kPeriod * kCycles + kTimerSlack); - EXPECT_GE(counted_signals.load(), kCycles); -} - -TEST(IntervalTimerTest, PeriodicThreadDirectedSignal) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Block kSigno so that we can accumulate overruns. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - absl::SleepFor(kPeriod * kCycles + kTimerSlack); - - // At least kCycles expirations should have occurred, resulting in kCycles-1 - // overruns (the first expiration sent the signal successfully). - siginfo_t si; - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - EXPECT_GE(si.si_overrun, kCycles - 1); - EXPECT_EQ(si.si_int, kSigvalue); - - // Kill the timer, then drain any additional signal it may have enqueued. We - // can't do this before the preceding sigtimedwait because stopping or - // deleting the timer resets si_overrun to 0. - timer.reset(); - sigtimedwait(&mask, &si, &zero_ts); -} - -TEST(IntervalTimerTest, OtherThreadGroup) { - constexpr int kSigno = SIGUSR1; - - // Create a subprocess that does nothing until killed. - pid_t child_pid; - const auto sp = ASSERT_NO_ERRNO_AND_VALUE(ForkAndExec( - "/proc/self/exe", ExecveArray({"timers", "--timers_test_sleep"}), - ExecveArray(), &child_pid, nullptr)); - - // Verify that we can't create a timer that would send signals to it. - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_notify_thread_id = child_pid; - EXPECT_THAT(TimerCreate(CLOCK_MONOTONIC, sev), PosixErrorIs(EINVAL, _)); -} - -TEST(IntervalTimerTest, RealTimeSignalsAreNotDuplicated) { - const int kSigno = SIGRTMIN; - constexpr int kSigvalue = 42; - - // Block signo so that we can accumulate overruns. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - const auto scoped_sigmask = ScopedSignalMask(SIG_BLOCK, mask); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - const auto timer = - ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - absl::SleepFor(kPeriod * kCycles + kTimerSlack); - - // Stop the timer so that no further signals are enqueued after sigtimedwait. - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - its.it_value = its.it_interval = zero_ts; - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // The timer should have sent only a single signal, even though the kernel - // supports enqueueing of multiple RT signals. - siginfo_t si; - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - // si_overrun was reset by timer_settime. - EXPECT_EQ(si.si_overrun, 0); - EXPECT_EQ(si.si_int, kSigvalue); - EXPECT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST(IntervalTimerTest, AlreadyPendingSignal) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Block kSigno so that we can accumulate overruns. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - const auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - // Send ourselves a signal, preventing the timer from enqueuing. - ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds()); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // End the sleep one cycle short; we will sleep for one more cycle below. - absl::SleepFor(kPeriod * (kCycles - 1)); - - // Dequeue the first signal, which we sent to ourselves with tgkill. - siginfo_t si; - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - // glibc sigtimedwait silently replaces SI_TKILL with SI_USER: - // sysdeps/unix/sysv/linux/sigtimedwait.c:__sigtimedwait(). This isn't - // documented, so we don't depend on it. - EXPECT_THAT(si.si_code, AnyOf(SI_USER, SI_TKILL)); - - // Sleep for 1 more cycle to give the timer time to send a signal. - absl::SleepFor(kPeriod + kTimerSlack); - - // At least kCycles expirations should have occurred, resulting in kCycles-1 - // overruns (the last expiration sent the signal successfully). - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - EXPECT_GE(si.si_overrun, kCycles - 1); - EXPECT_EQ(si.si_int, kSigvalue); - - // Kill the timer, then drain any additional signal it may have enqueued. We - // can't do this before the preceding sigtimedwait because stopping or - // deleting the timer resets si_overrun to 0. - timer.reset(); - sigtimedwait(&mask, &si, &zero_ts); -} - -TEST(IntervalTimerTest, IgnoredSignalCountsAsOverrun) { - constexpr int kSigno = SIGUSR1; - constexpr int kSigvalue = 42; - - // Ignore kSigno. - struct sigaction sa = {}; - sa.sa_handler = SIG_IGN; - const auto scoped_sigaction = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kSigno, sa)); - - // Unblock kSigno so that ignored signals will be discarded. - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, kSigno); - auto scoped_sigmask = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_UNBLOCK, mask)); - - struct sigevent sev = {}; - sev.sigev_notify = SIGEV_THREAD_ID; - sev.sigev_signo = kSigno; - sev.sigev_value.sival_int = kSigvalue; - sev.sigev_notify_thread_id = gettid(); - auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev)); - - constexpr absl::Duration kPeriod = absl::Seconds(1); - constexpr int kCycles = 3; - struct itimerspec its = {}; - its.it_value = its.it_interval = absl::ToTimespec(kPeriod); - ASSERT_NO_ERRNO(timer.Set(0, its)); - - // End the sleep one cycle short; we will sleep for one more cycle below. - absl::SleepFor(kPeriod * (kCycles - 1)); - - // Block kSigno so that ignored signals will be enqueued. - scoped_sigmask.Release()(); - scoped_sigmask = ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, mask)); - - // Sleep for 1 more cycle to give the timer time to send a signal. - absl::SleepFor(kPeriod + kTimerSlack); - - // At least kCycles expirations should have occurred, resulting in kCycles-1 - // overruns (the last expiration sent the signal successfully). - siginfo_t si; - struct timespec zero_ts = absl::ToTimespec(absl::ZeroDuration()); - ASSERT_THAT(sigtimedwait(&mask, &si, &zero_ts), - SyscallSucceedsWithValue(kSigno)); - EXPECT_EQ(si.si_signo, kSigno); - EXPECT_EQ(si.si_code, SI_TIMER); - EXPECT_EQ(si.si_timerid, timer.get()); - EXPECT_GE(si.si_overrun, kCycles - 1); - EXPECT_EQ(si.si_int, kSigvalue); - - // Kill the timer, then drain any additional signal it may have enqueued. We - // can't do this before the preceding sigtimedwait because stopping or - // deleting the timer resets si_overrun to 0. - timer.reset(); - sigtimedwait(&mask, &si, &zero_ts); -} - -} // namespace -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_timers_test_sleep)) { - while (true) { - absl::SleepFor(absl::Seconds(10)); - } - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/tkill.cc b/test/syscalls/linux/tkill.cc deleted file mode 100644 index 8d8ebbb24..000000000 --- a/test/syscalls/linux/tkill.cc +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include <cerrno> -#include <csignal> - -#include "gtest/gtest.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -static int tkill(pid_t tid, int sig) { - int ret; - do { - // NOTE(b/25434735): tkill(2) could return EAGAIN for RT signals. - ret = syscall(SYS_tkill, tid, sig); - } while (ret == -1 && errno == EAGAIN); - return ret; -} - -TEST(TkillTest, InvalidTID) { - EXPECT_THAT(tkill(-1, 0), SyscallFailsWithErrno(EINVAL)); - EXPECT_THAT(tkill(0, 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(TkillTest, ValidTID) { - EXPECT_THAT(tkill(gettid(), 0), SyscallSucceeds()); -} - -void SigHandler(int sig, siginfo_t* info, void* context) { - TEST_CHECK(sig == SIGRTMAX); - TEST_CHECK(info->si_pid == getpid()); - TEST_CHECK(info->si_uid == getuid()); - TEST_CHECK(info->si_code == SI_TKILL); -} - -// Test with a real signal. Regression test for b/24790092. -TEST(TkillTest, ValidTIDAndRealSignal) { - struct sigaction sa; - sa.sa_sigaction = SigHandler; - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - ASSERT_THAT(sigaction(SIGRTMAX, &sa, nullptr), SyscallSucceeds()); - // InitGoogle blocks all RT signals, so we need undo it. - sigset_t unblock; - sigemptyset(&unblock); - sigaddset(&unblock, SIGRTMAX); - ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &unblock, nullptr), SyscallSucceeds()); - EXPECT_THAT(tkill(gettid(), SIGRTMAX), SyscallSucceeds()); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/truncate.cc b/test/syscalls/linux/truncate.cc deleted file mode 100644 index 17832c47d..000000000 --- a/test/syscalls/linux/truncate.cc +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <signal.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/vfs.h> -#include <time.h> -#include <unistd.h> - -#include <iostream> -#include <string> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/syscalls/linux/file_base.h" -#include "test/util/capability_util.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 { - -class FixtureTruncateTest : public FileTest { - void SetUp() override { FileTest::SetUp(); } -}; - -TEST_F(FixtureTruncateTest, Truncate) { - // Get the current rlimit and restore after test run. - struct rlimit initial_lim; - ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - auto cleanup = Cleanup([&initial_lim] { - EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - }); - - // Check that it starts at size zero. - struct stat buf; - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); - - // Stay at size zero. - EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); - - // Grow to ten bytes. - EXPECT_THAT(truncate(test_file_name_.c_str(), 10), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 10); - - // Can't be truncated to a negative number. - EXPECT_THAT(truncate(test_file_name_.c_str(), -1), - SyscallFailsWithErrno(EINVAL)); - - // Try growing past the file size limit. - sigset_t new_mask; - sigemptyset(&new_mask); - sigaddset(&new_mask, SIGXFSZ); - sigprocmask(SIG_BLOCK, &new_mask, nullptr); - struct timespec timelimit; - timelimit.tv_sec = 10; - timelimit.tv_nsec = 0; - - struct rlimit setlim; - setlim.rlim_cur = 1024; - setlim.rlim_max = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); - EXPECT_THAT(truncate(test_file_name_.c_str(), 1025), - SyscallFailsWithErrno(EFBIG)); - EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ); - ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds()); - - // Shrink back down to zero. - EXPECT_THAT(truncate(test_file_name_.c_str(), 0), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); -} - -TEST_F(FixtureTruncateTest, Ftruncate) { - // Get the current rlimit and restore after test run. - struct rlimit initial_lim; - ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - auto cleanup = Cleanup([&initial_lim] { - EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - }); - - // Check that it starts at size zero. - struct stat buf; - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); - - // Stay at size zero. - EXPECT_THAT(ftruncate(test_file_fd_.get(), 0), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); - - // Grow to ten bytes. - EXPECT_THAT(ftruncate(test_file_fd_.get(), 10), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 10); - - // Can't be truncated to a negative number. - EXPECT_THAT(ftruncate(test_file_fd_.get(), -1), - SyscallFailsWithErrno(EINVAL)); - - // Try growing past the file size limit. - sigset_t new_mask; - sigemptyset(&new_mask); - sigaddset(&new_mask, SIGXFSZ); - sigprocmask(SIG_BLOCK, &new_mask, nullptr); - struct timespec timelimit; - timelimit.tv_sec = 10; - timelimit.tv_nsec = 0; - - struct rlimit setlim; - setlim.rlim_cur = 1024; - setlim.rlim_max = RLIM_INFINITY; - ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); - EXPECT_THAT(ftruncate(test_file_fd_.get(), 1025), - SyscallFailsWithErrno(EFBIG)); - EXPECT_EQ(sigtimedwait(&new_mask, nullptr, &timelimit), SIGXFSZ); - ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &new_mask, nullptr), SyscallSucceeds()); - - // Shrink back down to zero. - EXPECT_THAT(ftruncate(test_file_fd_.get(), 0), SyscallSucceeds()); - ASSERT_THAT(fstat(test_file_fd_.get(), &buf), SyscallSucceeds()); - EXPECT_EQ(buf.st_size, 0); -} - -// Truncating a file down clears that portion of the file. -TEST_F(FixtureTruncateTest, FtruncateShrinkGrow) { - std::vector<char> buf(10, 'a'); - EXPECT_THAT(WriteFd(test_file_fd_.get(), buf.data(), buf.size()), - SyscallSucceedsWithValue(buf.size())); - - // Shrink then regrow the file. This should clear the second half of the file. - EXPECT_THAT(ftruncate(test_file_fd_.get(), 5), SyscallSucceeds()); - EXPECT_THAT(ftruncate(test_file_fd_.get(), 10), SyscallSucceeds()); - - EXPECT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), SyscallSucceeds()); - - std::vector<char> buf2(10); - EXPECT_THAT(ReadFd(test_file_fd_.get(), buf2.data(), buf2.size()), - SyscallSucceedsWithValue(buf2.size())); - - std::vector<char> expect = {'a', 'a', 'a', 'a', 'a', - '\0', '\0', '\0', '\0', '\0'}; - EXPECT_EQ(expect, buf2); -} - -TEST(TruncateTest, TruncateDir) { - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(truncate(temp_dir.path().c_str(), 0), - SyscallFailsWithErrno(EISDIR)); -} - -TEST(TruncateTest, FtruncateDir) { - auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_dir.path(), O_DIRECTORY | O_RDONLY)); - EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(TruncateTest, TruncateNonWriteable) { - // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to - // always override write permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */)); - EXPECT_THAT(truncate(temp_file.path().c_str(), 0), - SyscallFailsWithErrno(EACCES)); -} - -TEST(TruncateTest, FtruncateNonWriteable) { - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDONLY)); - EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL)); -} - -TEST(TruncateTest, FtruncateWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */)); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_PATH)); - EXPECT_THAT(ftruncate(fd.get(), 0), AnyOf(SyscallFailsWithErrno(EBADF), - SyscallFailsWithErrno(EINVAL))); -} - -// ftruncate(2) should succeed as long as the file descriptor is writeable, -// regardless of whether the file permissions allow writing. -TEST(TruncateTest, FtruncateWithoutWritePermission_NoRandomSave) { - // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - - // The only time we can open a file with flags forbidden by its permissions - // is when we are creating the file. We cannot re-open with the same flags, - // so we cannot restore an fd obtained from such an operation. - const DisableSave ds; - auto path = NewTempAbsPath(); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDWR | O_CREAT, 0444)); - - // In goferfs, ftruncate may be converted to a remote truncate operation that - // unavoidably requires write permission. - SKIP_IF(IsRunningOnGvisor() && !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(path))); - ASSERT_THAT(ftruncate(fd.get(), 100), SyscallSucceeds()); -} - -TEST(TruncateTest, TruncateNonExist) { - EXPECT_THAT(truncate("/foo/bar", 0), SyscallFailsWithErrno(ENOENT)); -} - -TEST(TruncateTest, FtruncateVirtualTmp_NoRandomSave) { - auto temp_file = NewTempAbsPathInDir("/dev/shm"); - const DisableSave ds; // Incompatible permissions. - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file, O_RDWR | O_CREAT | O_EXCL, 0)); - EXPECT_THAT(ftruncate(fd.get(), 100), SyscallSucceeds()); -} - -// NOTE: There are additional truncate(2)/ftruncate(2) tests in mknod.cc -// which are there to avoid running the tests on a number of different -// filesystems which may not support mknod. - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc deleted file mode 100644 index 13ed0d68a..000000000 --- a/test/syscalls/linux/tuntap.cc +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2019 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 <arpa/inet.h> -#include <linux/capability.h> -#include <linux/if_arp.h> -#include <linux/if_ether.h> -#include <linux/if_tun.h> -#include <netinet/ip.h> -#include <netinet/ip_icmp.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/ascii.h" -#include "absl/strings/str_split.h" -#include "test/syscalls/linux/socket_netlink_route_util.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { -namespace { - -constexpr int kIPLen = 4; - -constexpr const char kDevNetTun[] = "/dev/net/tun"; -constexpr const char kTapName[] = "tap0"; - -#define kTapIPAddr htonl(0x0a000001) /* Inet 10.0.0.1 */ -#define kTapPeerIPAddr htonl(0x0a000002) /* Inet 10.0.0.2 */ - -constexpr const uint8_t kMacA[ETH_ALEN] = {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}; -constexpr const uint8_t kMacB[ETH_ALEN] = {0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB}; - -PosixErrorOr<std::set<std::string>> DumpLinkNames() { - ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); - std::set<std::string> names; - for (const auto& link : links) { - names.emplace(link.name); - } - return names; -} - -PosixErrorOr<Link> GetLinkByName(const std::string& name) { - ASSIGN_OR_RETURN_ERRNO(auto links, DumpLinks()); - for (const auto& link : links) { - if (link.name == name) { - return link; - } - } - return PosixError(ENOENT, "interface not found"); -} - -struct pihdr { - uint16_t pi_flags; - uint16_t pi_protocol; -} __attribute__((packed)); - -struct ping_pkt { - pihdr pi; - struct ethhdr eth; - struct iphdr ip; - struct icmphdr icmp; - char payload[64]; -} __attribute__((packed)); - -ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip, - const uint8_t dstmac[ETH_ALEN], - const in_addr_t dstip) { - ping_pkt pkt = {}; - - pkt.pi.pi_protocol = htons(ETH_P_IP); - - memcpy(pkt.eth.h_dest, dstmac, sizeof(pkt.eth.h_dest)); - memcpy(pkt.eth.h_source, srcmac, sizeof(pkt.eth.h_source)); - pkt.eth.h_proto = htons(ETH_P_IP); - - pkt.ip.ihl = 5; - pkt.ip.version = 4; - pkt.ip.tos = 0; - pkt.ip.tot_len = htons(sizeof(struct iphdr) + sizeof(struct icmphdr) + - sizeof(pkt.payload)); - pkt.ip.id = 1; - pkt.ip.frag_off = 1 << 6; // Do not fragment - pkt.ip.ttl = 64; - pkt.ip.protocol = IPPROTO_ICMP; - pkt.ip.daddr = dstip; - pkt.ip.saddr = srcip; - pkt.ip.check = IPChecksum(pkt.ip); - - pkt.icmp.type = ICMP_ECHO; - pkt.icmp.code = 0; - pkt.icmp.checksum = 0; - pkt.icmp.un.echo.sequence = 1; - pkt.icmp.un.echo.id = 1; - - strncpy(pkt.payload, "abcd", sizeof(pkt.payload)); - pkt.icmp.checksum = ICMPChecksum(pkt.icmp, pkt.payload, sizeof(pkt.payload)); - - return pkt; -} - -struct arp_pkt { - pihdr pi; - struct ethhdr eth; - struct arphdr arp; - uint8_t arp_sha[ETH_ALEN]; - uint8_t arp_spa[kIPLen]; - uint8_t arp_tha[ETH_ALEN]; - uint8_t arp_tpa[kIPLen]; -} __attribute__((packed)); - -std::string CreateArpPacket(const uint8_t srcmac[ETH_ALEN], - const in_addr_t srcip, - const uint8_t dstmac[ETH_ALEN], - const in_addr_t dstip) { - std::string buffer; - buffer.resize(sizeof(arp_pkt)); - - arp_pkt* pkt = reinterpret_cast<arp_pkt*>(&buffer[0]); - { - pkt->pi.pi_protocol = htons(ETH_P_ARP); - - memcpy(pkt->eth.h_dest, kMacA, sizeof(pkt->eth.h_dest)); - memcpy(pkt->eth.h_source, kMacB, sizeof(pkt->eth.h_source)); - pkt->eth.h_proto = htons(ETH_P_ARP); - - pkt->arp.ar_hrd = htons(ARPHRD_ETHER); - pkt->arp.ar_pro = htons(ETH_P_IP); - pkt->arp.ar_hln = ETH_ALEN; - pkt->arp.ar_pln = kIPLen; - pkt->arp.ar_op = htons(ARPOP_REPLY); - - memcpy(pkt->arp_sha, srcmac, sizeof(pkt->arp_sha)); - memcpy(pkt->arp_spa, &srcip, sizeof(pkt->arp_spa)); - memcpy(pkt->arp_tha, dstmac, sizeof(pkt->arp_tha)); - memcpy(pkt->arp_tpa, &dstip, sizeof(pkt->arp_tpa)); - } - return buffer; -} - -} // namespace - -TEST(TuntapStaticTest, NetTunExists) { - struct stat statbuf; - ASSERT_THAT(stat(kDevNetTun, &statbuf), SyscallSucceeds()); - // Check that it's a character device with rw-rw-rw- permissions. - EXPECT_EQ(statbuf.st_mode, S_IFCHR | 0666); -} - -class TuntapTest : public ::testing::Test { - protected: - void SetUp() override { - have_net_admin_cap_ = - ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)); - - if (have_net_admin_cap_ && !IsRunningOnGvisor()) { - // gVisor always creates enabled/up'd interfaces, while Linux does not (as - // observed in b/110961832). Some of the tests require the Linux stack to - // notify the socket of any link-address-resolution failures. Those - // notifications do not seem to show up when the loopback interface in the - // namespace is down. - auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName("lo")); - ASSERT_NO_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP)); - } - } - - void TearDown() override { - if (have_net_admin_cap_) { - // Bring back capability if we had dropped it in test case. - ASSERT_NO_ERRNO(SetCapability(CAP_NET_ADMIN, true)); - } - } - - bool have_net_admin_cap_; -}; - -TEST_F(TuntapTest, CreateInterfaceNoCap) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - ASSERT_NO_ERRNO(SetCapability(CAP_NET_ADMIN, false)); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); - - struct ifreq ifr = {}; - ifr.ifr_flags = IFF_TAP; - strncpy(ifr.ifr_name, kTapName, IFNAMSIZ); - - EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallFailsWithErrno(EPERM)); -} - -TEST_F(TuntapTest, CreateFixedNameInterface) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); - - struct ifreq ifr_set = {}; - ifr_set.ifr_flags = IFF_TAP; - strncpy(ifr_set.ifr_name, kTapName, IFNAMSIZ); - EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set), - SyscallSucceedsWithValue(0)); - - struct ifreq ifr_get = {}; - EXPECT_THAT(ioctl(fd.get(), TUNGETIFF, &ifr_get), - SyscallSucceedsWithValue(0)); - - struct ifreq ifr_expect = ifr_set; - // See __tun_chr_ioctl() in net/drivers/tun.c. - ifr_expect.ifr_flags |= IFF_NOFILTER; - - EXPECT_THAT(DumpLinkNames(), - IsPosixErrorOkAndHolds(::testing::Contains(kTapName))); - EXPECT_THAT(memcmp(&ifr_expect, &ifr_get, sizeof(ifr_get)), ::testing::Eq(0)); -} - -TEST_F(TuntapTest, CreateInterface) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); - - struct ifreq ifr = {}; - ifr.ifr_flags = IFF_TAP; - // Empty ifr.ifr_name. Let kernel assign. - - EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallSucceedsWithValue(0)); - - struct ifreq ifr_get = {}; - EXPECT_THAT(ioctl(fd.get(), TUNGETIFF, &ifr_get), - SyscallSucceedsWithValue(0)); - - std::string ifname = ifr_get.ifr_name; - EXPECT_THAT(ifname, ::testing::StartsWith("tap")); - EXPECT_THAT(DumpLinkNames(), - IsPosixErrorOkAndHolds(::testing::Contains(ifname))); -} - -TEST_F(TuntapTest, InvalidReadWrite) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); - - char buf[128] = {}; - EXPECT_THAT(read(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EBADFD)); - EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EBADFD)); -} - -TEST_F(TuntapTest, WriteToDownDevice) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - // FIXME(b/110961832): gVisor always creates enabled/up'd interfaces. - SKIP_IF(IsRunningOnGvisor()); - - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); - - // Device created should be down by default. - struct ifreq ifr = {}; - ifr.ifr_flags = IFF_TAP; - EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallSucceedsWithValue(0)); - - char buf[128] = {}; - EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EIO)); -} - -PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name, - const in_addr_t dev_addr) { - // Interface creation. - ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(kDevNetTun, O_RDWR)); - - struct ifreq ifr_set = {}; - ifr_set.ifr_flags = IFF_TAP; - strncpy(ifr_set.ifr_name, dev_name.c_str(), IFNAMSIZ); - if (ioctl(fd.get(), TUNSETIFF, &ifr_set) < 0) { - return PosixError(errno); - } - - ASSIGN_OR_RETURN_ERRNO(auto link, GetLinkByName(dev_name)); - - const struct in_addr dev_ipv4_addr = {.s_addr = dev_addr}; - // Interface setup. - EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, /*prefixlen=*/24, - &dev_ipv4_addr, sizeof(dev_ipv4_addr))); - - if (!IsRunningOnGvisor()) { - // FIXME(b/110961832): gVisor doesn't support setting MAC address on - // interfaces yet. - RETURN_IF_ERRNO(LinkSetMacAddr(link.index, kMacA, sizeof(kMacA))); - - // FIXME(b/110961832): gVisor always creates enabled/up'd interfaces. - RETURN_IF_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP)); - } - - return fd; -} - -// This test sets up a TAP device and pings kernel by sending ICMP echo request. -// -// It works as the following: -// * Open /dev/net/tun, and create kTapName interface. -// * Use rtnetlink to do initial setup of the interface: -// * Assign IP address 10.0.0.1/24 to kernel. -// * MAC address: kMacA -// * Bring up the interface. -// * Send an ICMP echo reqest (ping) packet from 10.0.0.2 (kMacB) to kernel. -// * Loop to receive packets from TAP device/fd: -// * If packet is an ICMP echo reply, it stops and passes the test. -// * If packet is an ARP request, it responds with canned reply and resends -// the -// ICMP request packet. -TEST_F(TuntapTest, PingKernel) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); - ping_pkt ping_req = - CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); - std::string arp_rep = - CreateArpPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr); - - // Send ping, this would trigger an ARP request on Linux. - EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)), - SyscallSucceedsWithValue(sizeof(ping_req))); - - // Receive loop to process inbound packets. - struct inpkt { - union { - pihdr pi; - ping_pkt ping; - arp_pkt arp; - }; - }; - while (1) { - inpkt r = {}; - int nread = read(fd.get(), &r, sizeof(r)); - EXPECT_THAT(nread, SyscallSucceeds()); - long unsigned int n = static_cast<long unsigned int>(nread); - - if (n < sizeof(pihdr)) { - std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol - << " len: " << n << std::endl; - continue; - } - - // Process ARP packet. - if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) { - // Respond with canned ARP reply. - EXPECT_THAT(write(fd.get(), arp_rep.data(), arp_rep.size()), - SyscallSucceedsWithValue(arp_rep.size())); - // First ping request might have been dropped due to mac address not in - // ARP cache. Send it again. - EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)), - SyscallSucceedsWithValue(sizeof(ping_req))); - } - - // Process ping response packet. - if (n >= sizeof(ping_pkt) && r.pi.pi_protocol == ping_req.pi.pi_protocol && - r.ping.ip.protocol == ping_req.ip.protocol && - !memcmp(&r.ping.ip.saddr, &ping_req.ip.daddr, kIPLen) && - !memcmp(&r.ping.ip.daddr, &ping_req.ip.saddr, kIPLen) && - r.ping.icmp.type == 0 && r.ping.icmp.code == 0) { - // Ends and passes the test. - break; - } - } -} - -TEST_F(TuntapTest, SendUdpTriggersArpResolution) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); - - // Send a UDP packet to remote. - int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - ASSERT_THAT(sock, SyscallSucceeds()); - - struct sockaddr_in remote = { - .sin_family = AF_INET, - .sin_port = htons(42), - .sin_addr = {.s_addr = kTapPeerIPAddr}, - }; - ASSERT_THAT(sendto(sock, "hello", 5, 0, reinterpret_cast<sockaddr*>(&remote), - sizeof(remote)), - SyscallSucceeds()); - - struct inpkt { - union { - pihdr pi; - arp_pkt arp; - }; - }; - while (1) { - inpkt r = {}; - int nread = read(fd.get(), &r, sizeof(r)); - EXPECT_THAT(nread, SyscallSucceeds()); - long unsigned int n = static_cast<long unsigned int>(nread); - - if (n < sizeof(pihdr)) { - std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol - << " len: " << n << std::endl; - continue; - } - - if (n >= sizeof(arp_pkt) && r.pi.pi_protocol == htons(ETH_P_ARP)) { - break; - } - } -} - -// TCPBlockingConnectFailsArpResolution tests for TCP connect to fail on link -// address resolution failure to a routable, but non existent peer. -TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor sender = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); - - sockaddr_in connect_addr = { - .sin_family = AF_INET, - .sin_addr = {.s_addr = kTapPeerIPAddr}, - }; - ASSERT_THAT(connect(sender.get(), - reinterpret_cast<const struct sockaddr*>(&connect_addr), - sizeof(connect_addr)), - SyscallFailsWithErrno(EHOSTUNREACH)); -} - -// TCPNonBlockingConnectFailsArpResolution tests for TCP non-blocking connect to -// to trigger an error event to be notified to poll on link address resolution -// failure to a routable, but non existent peer. -TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor sender = ASSERT_NO_ERRNO_AND_VALUE( - Socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP)); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); - - sockaddr_in connect_addr = { - .sin_family = AF_INET, - .sin_addr = {.s_addr = kTapPeerIPAddr}, - }; - ASSERT_THAT(connect(sender.get(), - reinterpret_cast<const struct sockaddr*>(&connect_addr), - sizeof(connect_addr)), - SyscallFailsWithErrno(EINPROGRESS)); - - constexpr int kTimeout = 10000; - struct pollfd pfd = { - .fd = sender.get(), - .events = POLLIN | POLLOUT, - }; - ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - ASSERT_EQ(pfd.revents, POLLIN | POLLOUT | POLLHUP | POLLERR); - - ASSERT_THAT(connect(sender.get(), - reinterpret_cast<const struct sockaddr*>(&connect_addr), - sizeof(connect_addr)), - SyscallFailsWithErrno(EHOSTUNREACH)); -} - -// Write hang bug found by syskaller: b/155928773 -// https://syzkaller.appspot.com/bug?id=065b893bd8d1d04a4e0a1d53c578537cde1efe99 -TEST_F(TuntapTest, WriteHangBug155928773) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr)); - - int sock = socket(AF_INET, SOCK_DGRAM, 0); - ASSERT_THAT(sock, SyscallSucceeds()); - - struct sockaddr_in remote = { - .sin_family = AF_INET, - .sin_port = htons(42), - .sin_addr = {.s_addr = kTapIPAddr}, - }; - // Return values do not matter in this test. - connect(sock, reinterpret_cast<struct sockaddr*>(&remote), sizeof(remote)); - write(sock, "hello", 5); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/tuntap_hostinet.cc b/test/syscalls/linux/tuntap_hostinet.cc deleted file mode 100644 index 1513fb9d5..000000000 --- a/test/syscalls/linux/tuntap_hostinet.cc +++ /dev/null @@ -1,38 +0,0 @@ -// 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 <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(TuntapHostInetTest, NoNetTun) { - SKIP_IF(!IsRunningOnGvisor()); - SKIP_IF(!IsRunningWithHostinet()); - - struct stat statbuf; - ASSERT_THAT(stat("/dev/net/tun", &statbuf), SyscallFailsWithErrno(ENOENT)); -} - -} // namespace -} // namespace testing - -} // namespace gvisor diff --git a/test/syscalls/linux/udp_bind.cc b/test/syscalls/linux/udp_bind.cc deleted file mode 100644 index 6d92bdbeb..000000000 --- a/test/syscalls/linux/udp_bind.cc +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include "gtest/gtest.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -struct sockaddr_in_common { - sa_family_t sin_family; - in_port_t sin_port; -}; - -struct SendtoTestParam { - // Human readable description of test parameter. - std::string description; - - // Test is broken in gVisor, skip. - bool skip_on_gvisor; - - // Domain for the socket that will do the sending. - int send_domain; - - // Address to bind for the socket that will do the sending. - struct sockaddr_storage send_addr; - socklen_t send_addr_len; // 0 for unbound. - - // Address to connect to for the socket that will do the sending. - struct sockaddr_storage connect_addr; - socklen_t connect_addr_len; // 0 for no connection. - - // Domain for the socket that will do the receiving. - int recv_domain; - - // Address to bind for the socket that will do the receiving. - struct sockaddr_storage recv_addr; - socklen_t recv_addr_len; - - // Address to send to. - struct sockaddr_storage sendto_addr; - socklen_t sendto_addr_len; - - // Expected errno for the sendto call. - std::vector<int> sendto_errnos; // empty on success. -}; - -class SendtoTest : public ::testing::TestWithParam<SendtoTestParam> { - protected: - SendtoTest() { - // gUnit uses printf, so so will we. - printf("Testing with %s\n", GetParam().description.c_str()); - } -}; - -TEST_P(SendtoTest, Sendto) { - auto param = GetParam(); - - SKIP_IF(param.skip_on_gvisor && IsRunningOnGvisor()); - - const FileDescriptor s1 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(param.send_domain, SOCK_DGRAM, 0)); - const FileDescriptor s2 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(param.recv_domain, SOCK_DGRAM, 0)); - - if (param.send_addr_len > 0) { - ASSERT_THAT(bind(s1.get(), reinterpret_cast<sockaddr*>(¶m.send_addr), - param.send_addr_len), - SyscallSucceeds()); - } - - if (param.connect_addr_len > 0) { - ASSERT_THAT( - connect(s1.get(), reinterpret_cast<sockaddr*>(¶m.connect_addr), - param.connect_addr_len), - SyscallSucceeds()); - } - - ASSERT_THAT(bind(s2.get(), reinterpret_cast<sockaddr*>(¶m.recv_addr), - param.recv_addr_len), - SyscallSucceeds()); - - struct sockaddr_storage real_recv_addr = {}; - socklen_t real_recv_addr_len = param.recv_addr_len; - ASSERT_THAT( - getsockname(s2.get(), reinterpret_cast<sockaddr*>(&real_recv_addr), - &real_recv_addr_len), - SyscallSucceeds()); - - ASSERT_EQ(real_recv_addr_len, param.recv_addr_len); - - int recv_port = - reinterpret_cast<sockaddr_in_common*>(&real_recv_addr)->sin_port; - - struct sockaddr_storage sendto_addr = param.sendto_addr; - reinterpret_cast<sockaddr_in_common*>(&sendto_addr)->sin_port = recv_port; - - char buf[20] = {}; - if (!param.sendto_errnos.empty()) { - ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr), - param.sendto_addr_len), - SyscallFailsWithErrno(ElementOf(param.sendto_errnos))); - return; - } - - ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0, - reinterpret_cast<sockaddr*>(&sendto_addr), - param.sendto_addr_len), - SyscallSucceedsWithValue(sizeof(buf))); - - struct sockaddr_storage got_addr = {}; - socklen_t got_addr_len = sizeof(sockaddr_storage); - ASSERT_THAT(RetryEINTR(recvfrom)(s2.get(), buf, sizeof(buf), 0, - reinterpret_cast<sockaddr*>(&got_addr), - &got_addr_len), - SyscallSucceedsWithValue(sizeof(buf))); - - ASSERT_GT(got_addr_len, sizeof(sockaddr_in_common)); - int got_port = reinterpret_cast<sockaddr_in_common*>(&got_addr)->sin_port; - - struct sockaddr_storage sender_addr = {}; - socklen_t sender_addr_len = sizeof(sockaddr_storage); - ASSERT_THAT(getsockname(s1.get(), reinterpret_cast<sockaddr*>(&sender_addr), - &sender_addr_len), - SyscallSucceeds()); - - ASSERT_GT(sender_addr_len, sizeof(sockaddr_in_common)); - int sender_port = - reinterpret_cast<sockaddr_in_common*>(&sender_addr)->sin_port; - - EXPECT_EQ(got_port, sender_port); -} - -socklen_t Ipv4Addr(sockaddr_storage* addr, int port = 0) { - auto addr4 = reinterpret_cast<sockaddr_in*>(addr); - addr4->sin_family = AF_INET; - addr4->sin_port = port; - inet_pton(AF_INET, "127.0.0.1", &addr4->sin_addr.s_addr); - return sizeof(struct sockaddr_in); -} - -socklen_t Ipv6Addr(sockaddr_storage* addr, int port = 0) { - auto addr6 = reinterpret_cast<sockaddr_in6*>(addr); - addr6->sin6_family = AF_INET6; - addr6->sin6_port = port; - inet_pton(AF_INET6, "::1", &addr6->sin6_addr.s6_addr); - return sizeof(struct sockaddr_in6); -} - -socklen_t Ipv4MappedIpv6Addr(sockaddr_storage* addr, int port = 0) { - auto addr6 = reinterpret_cast<sockaddr_in6*>(addr); - addr6->sin6_family = AF_INET6; - addr6->sin6_port = port; - inet_pton(AF_INET6, "::ffff:127.0.0.1", &addr6->sin6_addr.s6_addr); - return sizeof(struct sockaddr_in6); -} - -INSTANTIATE_TEST_SUITE_P( - UdpBindTest, SendtoTest, - ::testing::Values( - []() { - SendtoTestParam param = {}; - param.description = "IPv4 mapped IPv6 sendto IPv4 mapped IPv6"; - param.send_domain = AF_INET6; - param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "IPv6 sendto IPv6"; - param.send_domain = AF_INET6; - param.send_addr_len = Ipv6Addr(¶m.send_addr); - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "IPv4 sendto IPv4"; - param.send_domain = AF_INET; - param.send_addr_len = Ipv4Addr(¶m.send_addr); - param.recv_domain = AF_INET; - param.recv_addr_len = Ipv4Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "IPv4 mapped IPv6 sendto IPv4"; - param.send_domain = AF_INET6; - param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); - param.recv_domain = AF_INET; - param.recv_addr_len = Ipv4Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "IPv4 sendto IPv4 mapped IPv6"; - param.send_domain = AF_INET; - param.send_addr_len = Ipv4Addr(¶m.send_addr); - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "unbound IPv6 sendto IPv4 mapped IPv6"; - param.send_domain = AF_INET6; - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "unbound IPv6 sendto IPv4"; - param.send_domain = AF_INET6; - param.recv_domain = AF_INET; - param.recv_addr_len = Ipv4Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "IPv6 sendto IPv4"; - param.send_domain = AF_INET6; - param.send_addr_len = Ipv6Addr(¶m.send_addr); - param.recv_domain = AF_INET; - param.recv_addr_len = Ipv4Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - param.sendto_errnos = {ENETUNREACH}; - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "IPv4 mapped IPv6 sendto IPv6"; - param.send_domain = AF_INET6; - param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr); - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); - param.sendto_errnos = {EAFNOSUPPORT}; - // The errno returned changed in Linux commit c8e6ad0829a723. - param.sendto_errnos = {EINVAL, EAFNOSUPPORT}; - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "connected IPv4 mapped IPv6 sendto IPv6"; - param.send_domain = AF_INET6; - param.connect_addr_len = - Ipv4MappedIpv6Addr(¶m.connect_addr, 5000); - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr); - // The errno returned changed in Linux commit c8e6ad0829a723. - param.sendto_errnos = {EINVAL, EAFNOSUPPORT}; - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "connected IPv6 sendto IPv4 mapped IPv6"; - // TODO(igudger): Determine if this inconsistent behavior is worth - // implementing. - param.skip_on_gvisor = true; - param.send_domain = AF_INET6; - param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000); - param.recv_domain = AF_INET6; - param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - return param; - }(), - []() { - SendtoTestParam param = {}; - param.description = "connected IPv6 sendto IPv4"; - // TODO(igudger): Determine if this inconsistent behavior is worth - // implementing. - param.skip_on_gvisor = true; - param.send_domain = AF_INET6; - param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000); - param.recv_domain = AF_INET; - param.recv_addr_len = Ipv4Addr(¶m.recv_addr); - param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr); - return param; - }())); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc deleted file mode 100644 index 16eeeb5c6..000000000 --- a/test/syscalls/linux/udp_socket.cc +++ /dev/null @@ -1,2153 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <arpa/inet.h> -#include <fcntl.h> -#include <netinet/icmp6.h> -#include <netinet/ip_icmp.h> - -#include <ctime> - -#ifdef __linux__ -#include <linux/errqueue.h> -#include <linux/filter.h> -#endif // __linux__ -#include <netinet/in.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <sys/types.h> - -#include "absl/strings/str_format.h" -#ifndef SIOCGSTAMP -#include <linux/sockios.h> -#endif - -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/syscalls/linux/ip_socket_test_util.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/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Fixture for tests parameterized by the address family to use (AF_INET and -// AF_INET6) when creating sockets. -class UdpSocketTest - : public ::testing::TestWithParam<gvisor::testing::AddressFamily> { - protected: - // Creates two sockets that will be used by test cases. - void SetUp() override; - - // Binds the socket bind_ to the loopback and updates bind_addr_. - PosixError BindLoopback(); - - // Binds the socket bind_ to Any and updates bind_addr_. - PosixError BindAny(); - - // Binds given socket to address addr and updates. - PosixError BindSocket(int socket, struct sockaddr* addr); - - // Return initialized Any address to port 0. - struct sockaddr_storage InetAnyAddr(); - - // Return initialized Loopback address to port 0. - struct sockaddr_storage InetLoopbackAddr(); - - // Disconnects socket sockfd. - void Disconnect(int sockfd); - - // Get family for the test. - int GetFamily(); - - // Socket used by Bind methods - FileDescriptor bind_; - - // Second socket used for tests. - FileDescriptor sock_; - - // Address for bind_ socket. - struct sockaddr* bind_addr_; - - // Initialized to the length based on GetFamily(). - socklen_t addrlen_; - - // Storage for bind_addr_. - struct sockaddr_storage bind_addr_storage_; - - private: - // Helper to initialize addrlen_ for the test case. - socklen_t GetAddrLength(); -}; - -// Gets a pointer to the port component of the given address. -uint16_t* Port(struct sockaddr_storage* addr) { - switch (addr->ss_family) { - case AF_INET: { - auto sin = reinterpret_cast<struct sockaddr_in*>(addr); - return &sin->sin_port; - } - case AF_INET6: { - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(addr); - return &sin6->sin6_port; - } - } - - return nullptr; -} - -// Sets addr port to "port". -void SetPort(struct sockaddr_storage* addr, uint16_t port) { - switch (addr->ss_family) { - case AF_INET: { - auto sin = reinterpret_cast<struct sockaddr_in*>(addr); - sin->sin_port = port; - break; - } - case AF_INET6: { - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(addr); - sin6->sin6_port = port; - break; - } - } -} - -void UdpSocketTest::SetUp() { - addrlen_ = GetAddrLength(); - - bind_ = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, IPPROTO_UDP)); - memset(&bind_addr_storage_, 0, sizeof(bind_addr_storage_)); - bind_addr_ = reinterpret_cast<struct sockaddr*>(&bind_addr_storage_); - - sock_ = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, IPPROTO_UDP)); -} - -int UdpSocketTest::GetFamily() { - if (GetParam() == AddressFamily::kIpv4) { - return AF_INET; - } - return AF_INET6; -} - -PosixError UdpSocketTest::BindLoopback() { - bind_addr_storage_ = InetLoopbackAddr(); - struct sockaddr* bind_addr_ = - reinterpret_cast<struct sockaddr*>(&bind_addr_storage_); - return BindSocket(bind_.get(), bind_addr_); -} - -PosixError UdpSocketTest::BindAny() { - bind_addr_storage_ = InetAnyAddr(); - struct sockaddr* bind_addr_ = - reinterpret_cast<struct sockaddr*>(&bind_addr_storage_); - return BindSocket(bind_.get(), bind_addr_); -} - -PosixError UdpSocketTest::BindSocket(int socket, struct sockaddr* addr) { - socklen_t len = sizeof(bind_addr_storage_); - - // Bind, then check that we get the right address. - RETURN_ERROR_IF_SYSCALL_FAIL(bind(socket, addr, addrlen_)); - - RETURN_ERROR_IF_SYSCALL_FAIL(getsockname(socket, addr, &len)); - - if (addrlen_ != len) { - return PosixError( - EINVAL, - absl::StrFormat("getsockname len: %u expected: %u", len, addrlen_)); - } - return PosixError(0); -} - -socklen_t UdpSocketTest::GetAddrLength() { - struct sockaddr_storage addr; - if (GetFamily() == AF_INET) { - auto sin = reinterpret_cast<struct sockaddr_in*>(&addr); - return sizeof(*sin); - } - - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr); - return sizeof(*sin6); -} - -sockaddr_storage UdpSocketTest::InetAnyAddr() { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - reinterpret_cast<struct sockaddr*>(&addr)->sa_family = GetFamily(); - - if (GetFamily() == AF_INET) { - auto sin = reinterpret_cast<struct sockaddr_in*>(&addr); - sin->sin_addr.s_addr = htonl(INADDR_ANY); - sin->sin_port = htons(0); - return addr; - } - - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr); - sin6->sin6_addr = IN6ADDR_ANY_INIT; - sin6->sin6_port = htons(0); - return addr; -} - -sockaddr_storage UdpSocketTest::InetLoopbackAddr() { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - reinterpret_cast<struct sockaddr*>(&addr)->sa_family = GetFamily(); - - if (GetFamily() == AF_INET) { - auto sin = reinterpret_cast<struct sockaddr_in*>(&addr); - sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sin->sin_port = htons(0); - return addr; - } - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr); - sin6->sin6_addr = in6addr_loopback; - sin6->sin6_port = htons(0); - return addr; -} - -void UdpSocketTest::Disconnect(int sockfd) { - sockaddr_storage addr_storage = InetAnyAddr(); - sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - socklen_t addrlen = sizeof(addr_storage); - - addr->sa_family = AF_UNSPEC; - ASSERT_THAT(connect(sockfd, addr, addrlen), SyscallSucceeds()); - - // Check that after disconnect the socket is bound to the ANY address. - EXPECT_THAT(getsockname(sockfd, addr, &addrlen), SyscallSucceeds()); - if (GetParam() == AddressFamily::kIpv4) { - auto addr_out = reinterpret_cast<struct sockaddr_in*>(addr); - EXPECT_EQ(addrlen, sizeof(*addr_out)); - EXPECT_EQ(addr_out->sin_addr.s_addr, htonl(INADDR_ANY)); - } else { - auto addr_out = reinterpret_cast<struct sockaddr_in6*>(addr); - EXPECT_EQ(addrlen, sizeof(*addr_out)); - struct in6_addr loopback = IN6ADDR_ANY_INIT; - - EXPECT_EQ(memcmp(&addr_out->sin6_addr, &loopback, sizeof(in6_addr)), 0); - } -} - -TEST_P(UdpSocketTest, Creation) { - FileDescriptor sock = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, IPPROTO_UDP)); - EXPECT_THAT(close(sock.release()), SyscallSucceeds()); - - sock = ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, 0)); - EXPECT_THAT(close(sock.release()), SyscallSucceeds()); - - ASSERT_THAT(socket(GetFamily(), SOCK_STREAM, IPPROTO_UDP), SyscallFails()); -} - -TEST_P(UdpSocketTest, Getsockname) { - // Check that we're not bound. - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(bind_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - struct sockaddr_storage any = InetAnyAddr(); - EXPECT_EQ(memcmp(&addr, reinterpret_cast<struct sockaddr*>(&any), addrlen_), - 0); - - ASSERT_NO_ERRNO(BindLoopback()); - - EXPECT_THAT( - getsockname(bind_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(memcmp(&addr, bind_addr_, addrlen_), 0); -} - -TEST_P(UdpSocketTest, Getpeername) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Check that we're not connected. - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallFailsWithErrno(ENOTCONN)); - - // Connect, then check that we get the right address. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(memcmp(&addr, bind_addr_, addrlen_), 0); -} - -TEST_P(UdpSocketTest, SendNotConnected) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Do send & write, they must fail. - char buf[512]; - EXPECT_THAT(send(sock_.get(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(EDESTADDRREQ)); - - EXPECT_THAT(write(sock_.get(), buf, sizeof(buf)), - SyscallFailsWithErrno(EDESTADDRREQ)); - - // Use sendto. - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Check that we're bound now. - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_NE(*Port(&addr), 0); -} - -TEST_P(UdpSocketTest, ConnectBinds) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect the socket. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Check that we're bound now. - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_NE(*Port(&addr), 0); -} - -TEST_P(UdpSocketTest, ReceiveNotBound) { - char buf[512]; - EXPECT_THAT(recv(sock_.get(), buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST_P(UdpSocketTest, Bind) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Try to bind again. - EXPECT_THAT(bind(bind_.get(), bind_addr_, addrlen_), - SyscallFailsWithErrno(EINVAL)); - - // Check that we're still bound to the original address. - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(bind_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(memcmp(&addr, bind_addr_, addrlen_), 0); -} - -TEST_P(UdpSocketTest, BindInUse) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Try to bind again. - EXPECT_THAT(bind(sock_.get(), bind_addr_, addrlen_), - SyscallFailsWithErrno(EADDRINUSE)); -} - -TEST_P(UdpSocketTest, ConnectWriteToInvalidPort) { - // Discover a free unused port by creating a new UDP socket, binding it - // recording the just bound port and closing it. This is not guaranteed as it - // can still race with other port UDP sockets trying to bind a port at the - // same time. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - socklen_t addrlen = sizeof(addr_storage); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, IPPROTO_UDP)); - ASSERT_THAT(bind(s.get(), addr, addrlen), SyscallSucceeds()); - ASSERT_THAT(getsockname(s.get(), addr, &addrlen), SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_NE(*Port(&addr_storage), 0); - ASSERT_THAT(close(s.release()), SyscallSucceeds()); - - // Now connect to the port that we just released. This should generate an - // ECONNREFUSED error. - ASSERT_THAT(connect(sock_.get(), addr, addrlen_), SyscallSucceeds()); - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - // Send from sock_ to an unbound port. - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, addr, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Now verify that we got an ICMP error back of ECONNREFUSED. - int err; - socklen_t optlen = sizeof(err); - ASSERT_THAT(getsockopt(sock_.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), - SyscallSucceeds()); - ASSERT_EQ(err, ECONNREFUSED); - ASSERT_EQ(optlen, sizeof(err)); -} - -TEST_P(UdpSocketTest, ConnectSimultaneousWriteToInvalidPort) { - // Discover a free unused port by creating a new UDP socket, binding it - // recording the just bound port and closing it. This is not guaranteed as it - // can still race with other port UDP sockets trying to bind a port at the - // same time. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - socklen_t addrlen = sizeof(addr_storage); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - FileDescriptor s = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, IPPROTO_UDP)); - ASSERT_THAT(bind(s.get(), addr, addrlen), SyscallSucceeds()); - ASSERT_THAT(getsockname(s.get(), addr, &addrlen), SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_NE(*Port(&addr_storage), 0); - ASSERT_THAT(close(s.release()), SyscallSucceeds()); - - // Now connect to the port that we just released. - ScopedThread t([&] { - ASSERT_THAT(connect(sock_.get(), addr, addrlen_), SyscallSucceeds()); - }); - - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - // Send from sock_ to an unbound port. - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, addr, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - t.Join(); -} - -TEST_P(UdpSocketTest, ReceiveAfterConnect) { - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Send from sock_ to bind_ - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Receive the data. - char received[sizeof(buf)]; - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(sizeof(received))); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); -} - -TEST_P(UdpSocketTest, ReceiveAfterDisconnect) { - ASSERT_NO_ERRNO(BindLoopback()); - - for (int i = 0; i < 2; i++) { - // Connet sock_ to bound address. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - - // Send from sock to bind_. - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - - ASSERT_THAT(sendto(bind_.get(), buf, sizeof(buf), 0, - reinterpret_cast<sockaddr*>(&addr), addrlen), - SyscallSucceedsWithValue(sizeof(buf))); - - // Receive the data. - char received[sizeof(buf)]; - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(sizeof(received))); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); - - // Disconnect sock_. - struct sockaddr unspec = {}; - unspec.sa_family = AF_UNSPEC; - ASSERT_THAT(connect(sock_.get(), &unspec, sizeof(unspec.sa_family)), - SyscallSucceeds()); - } -} - -TEST_P(UdpSocketTest, Connect) { - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Check that we're connected to the right peer. - struct sockaddr_storage peer; - socklen_t peerlen = sizeof(peer); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&peer), &peerlen), - SyscallSucceeds()); - EXPECT_EQ(peerlen, addrlen_); - EXPECT_EQ(memcmp(&peer, bind_addr_, addrlen_), 0); - - // Try to bind after connect. - struct sockaddr_storage any = InetAnyAddr(); - EXPECT_THAT( - bind(sock_.get(), reinterpret_cast<struct sockaddr*>(&any), addrlen_), - SyscallFailsWithErrno(EINVAL)); - - struct sockaddr_storage bind2_storage = InetLoopbackAddr(); - struct sockaddr* bind2_addr = - reinterpret_cast<struct sockaddr*>(&bind2_storage); - FileDescriptor bind2 = - ASSERT_NO_ERRNO_AND_VALUE(Socket(GetFamily(), SOCK_DGRAM, IPPROTO_UDP)); - ASSERT_NO_ERRNO(BindSocket(bind2.get(), bind2_addr)); - - // Try to connect again. - EXPECT_THAT(connect(sock_.get(), bind2_addr, addrlen_), SyscallSucceeds()); - - // Check that peer name changed. - peerlen = sizeof(peer); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&peer), &peerlen), - SyscallSucceeds()); - EXPECT_EQ(peerlen, addrlen_); - EXPECT_EQ(memcmp(&peer, bind2_addr, addrlen_), 0); -} - -TEST_P(UdpSocketTest, ConnectAnyZero) { - // TODO(138658473): Enable when we can connect to port 0 with gVisor. - SKIP_IF(IsRunningOnGvisor()); - - struct sockaddr_storage any = InetAnyAddr(); - EXPECT_THAT( - connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&any), addrlen_), - SyscallSucceeds()); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(UdpSocketTest, ConnectAnyWithPort) { - ASSERT_NO_ERRNO(BindAny()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); -} - -TEST_P(UdpSocketTest, DisconnectAfterConnectAny) { - // TODO(138658473): Enable when we can connect to port 0 with gVisor. - SKIP_IF(IsRunningOnGvisor()); - struct sockaddr_storage any = InetAnyAddr(); - EXPECT_THAT( - connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&any), addrlen_), - SyscallSucceeds()); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallFailsWithErrno(ENOTCONN)); - - Disconnect(sock_.get()); -} - -TEST_P(UdpSocketTest, DisconnectAfterConnectAnyWithPort) { - ASSERT_NO_ERRNO(BindAny()); - EXPECT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(*Port(&bind_addr_storage_), *Port(&addr)); - - Disconnect(sock_.get()); -} - -TEST_P(UdpSocketTest, DisconnectAfterBind) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Bind to the next port above bind_. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_NO_ERRNO(BindSocket(sock_.get(), addr)); - - // Connect the socket. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - struct sockaddr_storage unspec = {}; - unspec.ss_family = AF_UNSPEC; - EXPECT_THAT(connect(sock_.get(), reinterpret_cast<sockaddr*>(&unspec), - sizeof(unspec.ss_family)), - SyscallSucceeds()); - - // Check that we're still bound. - socklen_t addrlen = sizeof(unspec); - EXPECT_THAT( - getsockname(sock_.get(), reinterpret_cast<sockaddr*>(&unspec), &addrlen), - SyscallSucceeds()); - - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(memcmp(addr, &unspec, addrlen_), 0); - - addrlen = sizeof(addr); - EXPECT_THAT(getpeername(sock_.get(), addr, &addrlen), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(UdpSocketTest, BindToAnyConnnectToLocalhost) { - ASSERT_NO_ERRNO(BindAny()); - - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - socklen_t addrlen = sizeof(addr); - - // Connect the socket. - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - EXPECT_THAT(getsockname(bind_.get(), addr, &addrlen), SyscallSucceeds()); - - // If the socket is bound to ANY and connected to a loopback address, - // getsockname() has to return the loopback address. - if (GetParam() == AddressFamily::kIpv4) { - auto addr_out = reinterpret_cast<struct sockaddr_in*>(addr); - EXPECT_EQ(addrlen, sizeof(*addr_out)); - EXPECT_EQ(addr_out->sin_addr.s_addr, htonl(INADDR_LOOPBACK)); - } else { - auto addr_out = reinterpret_cast<struct sockaddr_in6*>(addr); - struct in6_addr loopback = IN6ADDR_LOOPBACK_INIT; - EXPECT_EQ(addrlen, sizeof(*addr_out)); - EXPECT_EQ(memcmp(&addr_out->sin6_addr, &loopback, sizeof(in6_addr)), 0); - } -} - -TEST_P(UdpSocketTest, DisconnectAfterBindToAny) { - ASSERT_NO_ERRNO(BindLoopback()); - - struct sockaddr_storage any_storage = InetAnyAddr(); - struct sockaddr* any = reinterpret_cast<struct sockaddr*>(&any_storage); - SetPort(&any_storage, *Port(&bind_addr_storage_) + 1); - - ASSERT_NO_ERRNO(BindSocket(sock_.get(), any)); - - // Connect the socket. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - Disconnect(sock_.get()); - - // Check that we're still bound. - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(memcmp(&addr, any, addrlen), 0); - - addrlen = sizeof(addr); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(UdpSocketTest, Disconnect) { - ASSERT_NO_ERRNO(BindLoopback()); - - struct sockaddr_storage any_storage = InetAnyAddr(); - struct sockaddr* any = reinterpret_cast<struct sockaddr*>(&any_storage); - SetPort(&any_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_NO_ERRNO(BindSocket(sock_.get(), any)); - - for (int i = 0; i < 2; i++) { - // Try to connect again. - EXPECT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Check that we're connected to the right peer. - struct sockaddr_storage peer; - socklen_t peerlen = sizeof(peer); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&peer), &peerlen), - SyscallSucceeds()); - EXPECT_EQ(peerlen, addrlen_); - EXPECT_EQ(memcmp(&peer, bind_addr_, addrlen_), 0); - - // Try to disconnect. - struct sockaddr_storage addr = {}; - addr.ss_family = AF_UNSPEC; - EXPECT_THAT(connect(sock_.get(), reinterpret_cast<sockaddr*>(&addr), - sizeof(addr.ss_family)), - SyscallSucceeds()); - - peerlen = sizeof(peer); - EXPECT_THAT( - getpeername(sock_.get(), reinterpret_cast<sockaddr*>(&peer), &peerlen), - SyscallFailsWithErrno(ENOTCONN)); - - // Check that we're still bound. - socklen_t addrlen = sizeof(addr); - EXPECT_THAT( - getsockname(sock_.get(), reinterpret_cast<sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - EXPECT_EQ(addrlen, addrlen_); - EXPECT_EQ(*Port(&addr), *Port(&any_storage)); - } -} - -TEST_P(UdpSocketTest, ConnectBadAddress) { - struct sockaddr addr = {}; - addr.sa_family = GetFamily(); - ASSERT_THAT(connect(sock_.get(), &addr, sizeof(addr.sa_family)), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_P(UdpSocketTest, SendToAddressOtherThanConnected) { - ASSERT_NO_ERRNO(BindLoopback()); - - struct sockaddr_storage addr_storage = InetAnyAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Send to a different destination than we're connected to. - char buf[512]; - EXPECT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, addr, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST_P(UdpSocketTest, ConnectAndSendNoReceiver) { - ASSERT_NO_ERRNO(BindLoopback()); - // Close the socket to release the port so that we get an ICMP error. - ASSERT_THAT(close(bind_.release()), SyscallSucceeds()); - - // Connect to loopback:bind_addr_ which should *hopefully* not be bound by an - // UDP socket. There is no easy way to ensure that the UDP port is not bound - // by another conncurrently running test. *This is potentially flaky*. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - char buf[512]; - EXPECT_THAT(send(sock_.get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - constexpr int kTimeout = 1000; - // Poll to make sure we get the ICMP error back before issuing more writes. - struct pollfd pfd = {sock_.get(), POLLERR, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - - // Next write should fail with ECONNREFUSED due to the ICMP error generated in - // response to the previous write. - ASSERT_THAT(send(sock_.get(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(ECONNREFUSED)); - - // The next write should succeed again since the last write call would have - // retrieved and cleared the socket error. - ASSERT_THAT(send(sock_.get(), buf, sizeof(buf), 0), SyscallSucceeds()); - - // Poll to make sure we get the ICMP error back before issuing more writes. - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); - - // Next write should fail with ECONNREFUSED due to the ICMP error generated in - // response to the previous write. - ASSERT_THAT(send(sock_.get(), buf, sizeof(buf), 0), - SyscallFailsWithErrno(ECONNREFUSED)); -} - -#ifdef __linux__ -TEST_P(UdpSocketTest, RecvErrorConnRefused) { - // We will simulate an ICMP error and verify that we do receive that error via - // recvmsg(MSG_ERRQUEUE). - ASSERT_NO_ERRNO(BindLoopback()); - // Close the socket to release the port so that we get an ICMP error. - ASSERT_THAT(close(bind_.release()), SyscallSucceeds()); - - // Set IP_RECVERR socket option to enable error queueing. - int v = kSockOptOn; - socklen_t optlen = sizeof(v); - int opt_level = SOL_IP; - int opt_type = IP_RECVERR; - if (GetParam() != AddressFamily::kIpv4) { - opt_level = SOL_IPV6; - opt_type = IPV6_RECVERR; - } - ASSERT_THAT(setsockopt(sock_.get(), opt_level, opt_type, &v, optlen), - SyscallSucceeds()); - - // Connect to loopback:bind_addr_ which should *hopefully* not be bound by an - // UDP socket. There is no easy way to ensure that the UDP port is not bound - // by another conncurrently running test. *This is potentially flaky*. - const int kBufLen = 300; - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - char buf[kBufLen]; - RandomizeBuffer(buf, sizeof(buf)); - // Send from sock_ to an unbound port. This should cause ECONNREFUSED. - EXPECT_THAT(send(sock_.get(), buf, sizeof(buf), 0), - SyscallSucceedsWithValue(sizeof(buf))); - - // Dequeue error using recvmsg(MSG_ERRQUEUE). - char got[kBufLen]; - struct iovec iov; - iov.iov_base = reinterpret_cast<void*>(got); - iov.iov_len = kBufLen; - - size_t control_buf_len = CMSG_SPACE(sizeof(sock_extended_err) + addrlen_); - char* control_buf = static_cast<char*>(calloc(1, control_buf_len)); - struct sockaddr_storage remote; - memset(&remote, 0, sizeof(remote)); - struct msghdr msg = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_flags = 0; - msg.msg_control = control_buf; - msg.msg_controllen = control_buf_len; - msg.msg_name = reinterpret_cast<void*>(&remote); - msg.msg_namelen = addrlen_; - ASSERT_THAT(recvmsg(sock_.get(), &msg, MSG_ERRQUEUE), - SyscallSucceedsWithValue(kBufLen)); - - // Check the contents of msg. - EXPECT_EQ(memcmp(got, buf, sizeof(buf)), 0); // iovec check - // TODO(b/176251997): The next check fails on the gvisor platform due to the - // kernel bug. - if (!IsRunningWithHostinet() || GvisorPlatform() == Platform::kPtrace || - GvisorPlatform() == Platform::kKVM || - GvisorPlatform() == Platform::kNative) - EXPECT_NE(msg.msg_flags & MSG_ERRQUEUE, 0); - EXPECT_EQ(memcmp(&remote, bind_addr_, addrlen_), 0); - - // Check the contents of the control message. - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(CMSG_NXTHDR(&msg, cmsg), nullptr); - EXPECT_EQ(cmsg->cmsg_level, opt_level); - EXPECT_EQ(cmsg->cmsg_type, opt_type); - - // Check the contents of socket error. - struct sock_extended_err* sock_err = - (struct sock_extended_err*)CMSG_DATA(cmsg); - EXPECT_EQ(sock_err->ee_errno, ECONNREFUSED); - if (GetParam() == AddressFamily::kIpv4) { - EXPECT_EQ(sock_err->ee_origin, SO_EE_ORIGIN_ICMP); - EXPECT_EQ(sock_err->ee_type, ICMP_DEST_UNREACH); - EXPECT_EQ(sock_err->ee_code, ICMP_PORT_UNREACH); - } else { - EXPECT_EQ(sock_err->ee_origin, SO_EE_ORIGIN_ICMP6); - EXPECT_EQ(sock_err->ee_type, ICMP6_DST_UNREACH); - EXPECT_EQ(sock_err->ee_code, ICMP6_DST_UNREACH_NOPORT); - } - - // Now verify that the socket error was cleared by recvmsg(MSG_ERRQUEUE). - int err; - optlen = sizeof(err); - ASSERT_THAT(getsockopt(sock_.get(), SOL_SOCKET, SO_ERROR, &err, &optlen), - SyscallSucceeds()); - ASSERT_EQ(err, 0); - ASSERT_EQ(optlen, sizeof(err)); -} -#endif // __linux__ - -TEST_P(UdpSocketTest, ZerolengthWriteAllowed) { - // TODO(gvisor.dev/issue/1202): Hostinet does not support zero length writes. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - // Connect to loopback:bind_addr_+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Bind sock to loopback:bind_addr_+1. - ASSERT_THAT(bind(sock_.get(), addr, addrlen_), SyscallSucceeds()); - - char buf[3]; - // Send zero length packet from bind_ to sock_. - ASSERT_THAT(write(bind_.get(), buf, 0), SyscallSucceedsWithValue(0)); - - struct pollfd pfd = {sock_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout*/ 1000), - SyscallSucceedsWithValue(1)); - - // Receive the packet. - char received[3]; - EXPECT_THAT(read(sock_.get(), received, sizeof(received)), - SyscallSucceedsWithValue(0)); -} - -TEST_P(UdpSocketTest, ZerolengthWriteAllowedNonBlockRead) { - // TODO(gvisor.dev/issue/1202): Hostinet does not support zero length writes. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Bind sock to loopback:bind_addr_port+1. - ASSERT_THAT(bind(sock_.get(), addr, addrlen_), SyscallSucceeds()); - - // Set sock to non-blocking. - int opts = 0; - ASSERT_THAT(opts = fcntl(sock_.get(), F_GETFL), SyscallSucceeds()); - ASSERT_THAT(fcntl(sock_.get(), F_SETFL, opts | O_NONBLOCK), - SyscallSucceeds()); - - char buf[3]; - // Send zero length packet from bind_ to sock_. - ASSERT_THAT(write(bind_.get(), buf, 0), SyscallSucceedsWithValue(0)); - - struct pollfd pfd = {sock_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - // Receive the packet. - char received[3]; - EXPECT_THAT(read(sock_.get(), received, sizeof(received)), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(read(sock_.get(), received, sizeof(received)), - SyscallFailsWithErrno(EAGAIN)); -} - -TEST_P(UdpSocketTest, SendAndReceiveNotConnected) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Send some data to bind_. - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Receive the data. - char received[sizeof(buf)]; - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(sizeof(received))); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); -} - -TEST_P(UdpSocketTest, SendAndReceiveConnected) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Bind sock to loopback:bind_addr_port+1. - ASSERT_THAT(bind(sock_.get(), addr, addrlen_), SyscallSucceeds()); - - // Send some data from sock to bind_. - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Receive the data. - char received[sizeof(buf)]; - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(sizeof(received))); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); -} - -TEST_P(UdpSocketTest, ReceiveFromNotConnected) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Bind sock to loopback:bind_addr_port+2. - struct sockaddr_storage addr2_storage = InetLoopbackAddr(); - struct sockaddr* addr2 = reinterpret_cast<struct sockaddr*>(&addr2_storage); - SetPort(&addr2_storage, *Port(&bind_addr_storage_) + 2); - ASSERT_THAT(bind(sock_.get(), addr2, addrlen_), SyscallSucceeds()); - - // Send some data from sock to bind_. - char buf[512]; - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Check that the data isn't received because it was sent from a different - // address than we're connected. - EXPECT_THAT(recv(sock_.get(), buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST_P(UdpSocketTest, ReceiveBeforeConnect) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Bind sock to loopback:bind_addr_port+2. - struct sockaddr_storage addr2_storage = InetLoopbackAddr(); - struct sockaddr* addr2 = reinterpret_cast<struct sockaddr*>(&addr2_storage); - SetPort(&addr2_storage, *Port(&bind_addr_storage_) + 2); - ASSERT_THAT(bind(sock_.get(), addr2, addrlen_), SyscallSucceeds()); - - // Send some data from sock to bind_. - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Receive the data. It works because it was sent before the connect. - char received[sizeof(buf)]; - EXPECT_THAT( - RecvTimeout(bind_.get(), received, sizeof(received), 1 /*timeout*/), - IsPosixErrorOkAndHolds(sizeof(received))); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); - - // Send again. This time it should not be received. - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - EXPECT_THAT(recv(bind_.get(), buf, sizeof(buf), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST_P(UdpSocketTest, ReceiveFrom) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Bind sock to loopback:bind_addr_port+1. - ASSERT_THAT(bind(sock_.get(), addr, addrlen_), SyscallSucceeds()); - - // Send some data from sock to bind_. - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - - // Receive the data and sender address. - char received[sizeof(buf)]; - struct sockaddr_storage addr2; - socklen_t addr2len = sizeof(addr2); - EXPECT_THAT(recvfrom(bind_.get(), received, sizeof(received), 0, - reinterpret_cast<sockaddr*>(&addr2), &addr2len), - SyscallSucceedsWithValue(sizeof(received))); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); - EXPECT_EQ(addr2len, addrlen_); - EXPECT_EQ(memcmp(addr, &addr2, addrlen_), 0); -} - -TEST_P(UdpSocketTest, Listen) { - ASSERT_THAT(listen(sock_.get(), SOMAXCONN), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -TEST_P(UdpSocketTest, Accept) { - ASSERT_THAT(accept(sock_.get(), nullptr, nullptr), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -// This test validates that a read shutdown with pending data allows the read -// to proceed with the data before returning EAGAIN. -TEST_P(UdpSocketTest, ReadShutdownNonblockPendingData) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - // Bind to loopback:bind_addr_port+1 and connect to bind_addr_. - ASSERT_THAT(bind(sock_.get(), addr, addrlen_), SyscallSucceeds()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Verify that we get EWOULDBLOCK when there is nothing to read. - char received[512]; - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - const char* buf = "abc"; - EXPECT_THAT(write(sock_.get(), buf, 3), SyscallSucceedsWithValue(3)); - - int opts = 0; - ASSERT_THAT(opts = fcntl(bind_.get(), F_GETFL), SyscallSucceeds()); - ASSERT_THAT(fcntl(bind_.get(), F_SETFL, opts | O_NONBLOCK), - SyscallSucceeds()); - ASSERT_THAT(opts = fcntl(bind_.get(), F_GETFL), SyscallSucceeds()); - ASSERT_NE(opts & O_NONBLOCK, 0); - - EXPECT_THAT(shutdown(bind_.get(), SHUT_RD), SyscallSucceeds()); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - // We should get the data even though read has been shutdown. - EXPECT_THAT(RecvTimeout(bind_.get(), received, 2 /*buf_size*/, 1 /*timeout*/), - IsPosixErrorOkAndHolds(2)); - - // Because we read less than the entire packet length, since it's a packet - // based socket any subsequent reads should return EWOULDBLOCK. - EXPECT_THAT(recv(bind_.get(), received, 1, 0), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -// This test is validating that even after a socket is shutdown if it's -// reconnected it will reset the shutdown state. -TEST_P(UdpSocketTest, ReadShutdownSameSocketResetsShutdownState) { - char received[512]; - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - EXPECT_THAT(shutdown(bind_.get(), SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); - - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Connect the socket, then try to shutdown again. - ASSERT_NO_ERRNO(BindLoopback()); - - // Connect to loopback:bind_addr_port+1. - struct sockaddr_storage addr_storage = InetLoopbackAddr(); - struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_storage); - SetPort(&addr_storage, *Port(&bind_addr_storage_) + 1); - ASSERT_THAT(connect(bind_.get(), addr, addrlen_), SyscallSucceeds()); - - EXPECT_THAT(recv(bind_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); -} - -TEST_P(UdpSocketTest, ReadShutdown) { - // TODO(gvisor.dev/issue/1202): Calling recv() after shutdown without - // MSG_DONTWAIT blocks indefinitely. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - - char received[512]; - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - EXPECT_THAT(shutdown(sock_.get(), SHUT_RD), SyscallFailsWithErrno(ENOTCONN)); - - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Connect the socket, then try to shutdown again. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - EXPECT_THAT(shutdown(sock_.get(), SHUT_RD), SyscallSucceeds()); - - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(0)); -} - -TEST_P(UdpSocketTest, ReadShutdownDifferentThread) { - // TODO(gvisor.dev/issue/1202): Calling recv() after shutdown without - // MSG_DONTWAIT blocks indefinitely. - SKIP_IF(IsRunningWithHostinet()); - ASSERT_NO_ERRNO(BindLoopback()); - - char received[512]; - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Connect the socket, then shutdown from another thread. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - EXPECT_THAT(recv(sock_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - ScopedThread t([&] { - absl::SleepFor(absl::Milliseconds(200)); - EXPECT_THAT(shutdown(sock_.get(), SHUT_RD), SyscallSucceeds()); - }); - EXPECT_THAT(RetryEINTR(recv)(sock_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(0)); - t.Join(); - - EXPECT_THAT(RetryEINTR(recv)(sock_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(0)); -} - -TEST_P(UdpSocketTest, WriteShutdown) { - ASSERT_NO_ERRNO(BindLoopback()); - EXPECT_THAT(shutdown(sock_.get(), SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - EXPECT_THAT(shutdown(sock_.get(), SHUT_WR), SyscallSucceeds()); -} - -TEST_P(UdpSocketTest, SynchronousReceive) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Send some data to bind_ from another thread. - char buf[512]; - RandomizeBuffer(buf, sizeof(buf)); - - // Receive the data prior to actually starting the other thread. - char received[512]; - EXPECT_THAT( - RetryEINTR(recv)(bind_.get(), received, sizeof(received), MSG_DONTWAIT), - SyscallFailsWithErrno(EWOULDBLOCK)); - - // Start the thread. - ScopedThread t([&] { - absl::SleepFor(absl::Milliseconds(200)); - ASSERT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, this->bind_addr_, - this->addrlen_), - SyscallSucceedsWithValue(sizeof(buf))); - }); - - EXPECT_THAT(RetryEINTR(recv)(bind_.get(), received, sizeof(received), 0), - SyscallSucceedsWithValue(512)); - EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); -} - -TEST_P(UdpSocketTest, BoundaryPreserved_SendRecv) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Send 3 packets from sock to bind_. - constexpr int psize = 100; - char buf[3 * psize]; - RandomizeBuffer(buf, sizeof(buf)); - - for (int i = 0; i < 3; ++i) { - ASSERT_THAT( - sendto(sock_.get(), buf + i * psize, psize, 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(psize)); - } - - // Receive the data as 3 separate packets. - char received[6 * psize]; - for (int i = 0; i < 3; ++i) { - EXPECT_THAT(recv(bind_.get(), received + i * psize, 3 * psize, 0), - SyscallSucceedsWithValue(psize)); - } - EXPECT_EQ(memcmp(buf, received, 3 * psize), 0); -} - -TEST_P(UdpSocketTest, BoundaryPreserved_WritevReadv) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Direct writes from sock to bind_. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Send 2 packets from sock to bind_, where each packet's data consists of - // 2 discontiguous iovecs. - constexpr size_t kPieceSize = 100; - char buf[4 * kPieceSize]; - RandomizeBuffer(buf, sizeof(buf)); - - for (int i = 0; i < 2; i++) { - struct iovec iov[2]; - for (int j = 0; j < 2; j++) { - iov[j].iov_base = reinterpret_cast<void*>( - reinterpret_cast<uintptr_t>(buf) + (i + 2 * j) * kPieceSize); - iov[j].iov_len = kPieceSize; - } - ASSERT_THAT(writev(sock_.get(), iov, 2), - SyscallSucceedsWithValue(2 * kPieceSize)); - } - - // Receive the data as 2 separate packets. - char received[6 * kPieceSize]; - for (int i = 0; i < 2; i++) { - struct iovec iov[3]; - for (int j = 0; j < 3; j++) { - iov[j].iov_base = reinterpret_cast<void*>( - reinterpret_cast<uintptr_t>(received) + (i + 2 * j) * kPieceSize); - iov[j].iov_len = kPieceSize; - } - ASSERT_THAT(readv(bind_.get(), iov, 3), - SyscallSucceedsWithValue(2 * kPieceSize)); - } - EXPECT_EQ(memcmp(buf, received, 4 * kPieceSize), 0); -} - -TEST_P(UdpSocketTest, BoundaryPreserved_SendMsgRecvMsg) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Send 2 packets from sock to bind_, where each packet's data consists of - // 2 discontiguous iovecs. - constexpr size_t kPieceSize = 100; - char buf[4 * kPieceSize]; - RandomizeBuffer(buf, sizeof(buf)); - - for (int i = 0; i < 2; i++) { - struct iovec iov[2]; - for (int j = 0; j < 2; j++) { - iov[j].iov_base = reinterpret_cast<void*>( - reinterpret_cast<uintptr_t>(buf) + (i + 2 * j) * kPieceSize); - iov[j].iov_len = kPieceSize; - } - struct msghdr msg = {}; - msg.msg_name = bind_addr_; - msg.msg_namelen = addrlen_; - msg.msg_iov = iov; - msg.msg_iovlen = 2; - ASSERT_THAT(sendmsg(sock_.get(), &msg, 0), - SyscallSucceedsWithValue(2 * kPieceSize)); - } - - // Receive the data as 2 separate packets. - char received[6 * kPieceSize]; - for (int i = 0; i < 2; i++) { - struct iovec iov[3]; - for (int j = 0; j < 3; j++) { - iov[j].iov_base = reinterpret_cast<void*>( - reinterpret_cast<uintptr_t>(received) + (i + 2 * j) * kPieceSize); - iov[j].iov_len = kPieceSize; - } - struct msghdr msg = {}; - msg.msg_iov = iov; - msg.msg_iovlen = 3; - ASSERT_THAT(recvmsg(bind_.get(), &msg, 0), - SyscallSucceedsWithValue(2 * kPieceSize)); - } - EXPECT_EQ(memcmp(buf, received, 4 * kPieceSize), 0); -} - -TEST_P(UdpSocketTest, FIONREADShutdown) { - ASSERT_NO_ERRNO(BindLoopback()); - - int n = -1; - EXPECT_THAT(ioctl(sock_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - // A UDP socket must be connected before it can be shutdown. - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - n = -1; - EXPECT_THAT(ioctl(sock_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - EXPECT_THAT(shutdown(sock_.get(), SHUT_RD), SyscallSucceeds()); - - n = -1; - EXPECT_THAT(ioctl(sock_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); -} - -TEST_P(UdpSocketTest, FIONREADWriteShutdown) { - int n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - ASSERT_NO_ERRNO(BindLoopback()); - - // A UDP socket must be connected before it can be shutdown. - ASSERT_THAT(connect(bind_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - const char str[] = "abc"; - ASSERT_THAT(send(bind_.get(), str, sizeof(str), 0), - SyscallSucceedsWithValue(sizeof(str))); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, sizeof(str)); - - EXPECT_THAT(shutdown(bind_.get(), SHUT_RD), SyscallSucceeds()); - - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, sizeof(str)); -} - -// NOTE: Do not use `FIONREAD` as test name because it will be replaced by the -// corresponding macro and become `0x541B`. -TEST_P(UdpSocketTest, Fionread) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Check that the bound socket with an empty buffer reports an empty first - // packet. - int n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - // Send 3 packets from sock to bind_. - constexpr int psize = 100; - char buf[3 * psize]; - RandomizeBuffer(buf, sizeof(buf)); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - for (int i = 0; i < 3; ++i) { - ASSERT_THAT( - sendto(sock_.get(), buf + i * psize, psize, 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(psize)); - - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - // Check that regardless of how many packets are in the queue, the size - // reported is that of a single packet. - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, psize); - } -} - -TEST_P(UdpSocketTest, FIONREADZeroLengthPacket) { - ASSERT_NO_ERRNO(BindLoopback()); - - // Check that the bound socket with an empty buffer reports an empty first - // packet. - int n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - // Send 3 packets from sock to bind_. - constexpr int psize = 100; - char buf[3 * psize]; - RandomizeBuffer(buf, sizeof(buf)); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - for (int i = 0; i < 3; ++i) { - ASSERT_THAT( - sendto(sock_.get(), buf + i * psize, 0, 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(0)); - - // TODO(gvisor.dev/issue/2726): sending a zero-length message to a hostinet - // socket does not cause a poll event to be triggered. - if (!IsRunningWithHostinet()) { - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - } - - // Check that regardless of how many packets are in the queue, the size - // reported is that of a single packet. - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - } -} - -TEST_P(UdpSocketTest, FIONREADZeroLengthWriteShutdown) { - int n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - ASSERT_NO_ERRNO(BindLoopback()); - - // A UDP socket must be connected before it can be shutdown. - ASSERT_THAT(connect(bind_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - const char str[] = "abc"; - ASSERT_THAT(send(bind_.get(), str, 0, 0), SyscallSucceedsWithValue(0)); - - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); - - EXPECT_THAT(shutdown(bind_.get(), SHUT_RD), SyscallSucceeds()); - - n = -1; - EXPECT_THAT(ioctl(bind_.get(), FIONREAD, &n), SyscallSucceedsWithValue(0)); - EXPECT_EQ(n, 0); -} - -TEST_P(UdpSocketTest, SoNoCheckOffByDefault) { - // TODO(gvisor.dev/issue/1202): SO_NO_CHECK socket option not supported by - // hostinet. - SKIP_IF(IsRunningWithHostinet()); - - int v = -1; - socklen_t optlen = sizeof(v); - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_NO_CHECK, &v, &optlen), - SyscallSucceeds()); - ASSERT_EQ(v, kSockOptOff); - ASSERT_EQ(optlen, sizeof(v)); -} - -TEST_P(UdpSocketTest, SoNoCheck) { - // TODO(gvisor.dev/issue/1202): SO_NO_CHECK socket option not supported by - // hostinet. - SKIP_IF(IsRunningWithHostinet()); - - int v = kSockOptOn; - socklen_t optlen = sizeof(v); - ASSERT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_NO_CHECK, &v, optlen), - SyscallSucceeds()); - v = -1; - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_NO_CHECK, &v, &optlen), - SyscallSucceeds()); - ASSERT_EQ(v, kSockOptOn); - ASSERT_EQ(optlen, sizeof(v)); - - v = kSockOptOff; - ASSERT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_NO_CHECK, &v, optlen), - SyscallSucceeds()); - v = -1; - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_NO_CHECK, &v, &optlen), - SyscallSucceeds()); - ASSERT_EQ(v, kSockOptOff); - ASSERT_EQ(optlen, sizeof(v)); -} - -#ifdef __linux__ -TEST_P(UdpSocketTest, ErrorQueue) { - char cmsgbuf[CMSG_SPACE(sizeof(sock_extended_err))]; - msghdr msg; - memset(&msg, 0, sizeof(msg)); - iovec iov; - memset(&iov, 0, sizeof(iov)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - - // recv*(MSG_ERRQUEUE) never blocks, even without MSG_DONTWAIT. - EXPECT_THAT(RetryEINTR(recvmsg)(bind_.get(), &msg, MSG_ERRQUEUE), - SyscallFailsWithErrno(EAGAIN)); -} -#endif // __linux__ - -TEST_P(UdpSocketTest, SoTimestampOffByDefault) { - // TODO(gvisor.dev/issue/1202): SO_TIMESTAMP socket option not supported by - // hostinet. - SKIP_IF(IsRunningWithHostinet()); - - int v = -1; - socklen_t optlen = sizeof(v); - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_TIMESTAMP, &v, &optlen), - SyscallSucceeds()); - ASSERT_EQ(v, kSockOptOff); - ASSERT_EQ(optlen, sizeof(v)); -} - -TEST_P(UdpSocketTest, SoTimestamp) { - // TODO(gvisor.dev/issue/1202): ioctl() and SO_TIMESTAMP socket option are not - // supported by hostinet. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - int v = 1; - ASSERT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_TIMESTAMP, &v, sizeof(v)), - SyscallSucceeds()); - - char buf[3]; - // Send zero length packet from sock to bind_. - ASSERT_THAT(RetryEINTR(write)(sock_.get(), buf, 0), - SyscallSucceedsWithValue(0)); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - char cmsgbuf[CMSG_SPACE(sizeof(struct timeval))]; - msghdr msg; - memset(&msg, 0, sizeof(msg)); - iovec iov; - memset(&iov, 0, sizeof(iov)); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - - ASSERT_THAT(RetryEINTR(recvmsg)(bind_.get(), &msg, 0), - SyscallSucceedsWithValue(0)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SO_TIMESTAMP); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct timeval))); - - struct timeval tv = {}; - memcpy(&tv, CMSG_DATA(cmsg), sizeof(struct timeval)); - - ASSERT_TRUE(tv.tv_sec != 0 || tv.tv_usec != 0); - - // There should be nothing to get via ioctl. - ASSERT_THAT(ioctl(bind_.get(), SIOCGSTAMP, &tv), - SyscallFailsWithErrno(ENOENT)); -} - -TEST_P(UdpSocketTest, WriteShutdownNotConnected) { - EXPECT_THAT(shutdown(bind_.get(), SHUT_WR), SyscallFailsWithErrno(ENOTCONN)); -} - -TEST_P(UdpSocketTest, TimestampIoctl) { - // TODO(gvisor.dev/issue/1202): ioctl() is not supported by hostinet. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - char buf[3]; - // Send packet from sock to bind_. - ASSERT_THAT(RetryEINTR(write)(sock_.get(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - // There should be no control messages. - char recv_buf[sizeof(buf)]; - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(bind_.get(), recv_buf, sizeof(recv_buf))); - - // A nonzero timeval should be available via ioctl. - struct timeval tv = {}; - ASSERT_THAT(ioctl(bind_.get(), SIOCGSTAMP, &tv), SyscallSucceeds()); - ASSERT_TRUE(tv.tv_sec != 0 || tv.tv_usec != 0); -} - -TEST_P(UdpSocketTest, TimestampIoctlNothingRead) { - // TODO(gvisor.dev/issue/1202): ioctl() is not supported by hostinet. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - struct timeval tv = {}; - ASSERT_THAT(ioctl(sock_.get(), SIOCGSTAMP, &tv), - SyscallFailsWithErrno(ENOENT)); -} - -// Test that the timestamp accessed via SIOCGSTAMP is still accessible after -// SO_TIMESTAMP is enabled and used to retrieve a timestamp. -TEST_P(UdpSocketTest, TimestampIoctlPersistence) { - // TODO(gvisor.dev/issue/1202): ioctl() and SO_TIMESTAMP socket option are not - // supported by hostinet. - SKIP_IF(IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - char buf[3]; - // Send packet from sock to bind_. - ASSERT_THAT(RetryEINTR(write)(sock_.get(), buf, sizeof(buf)), - SyscallSucceedsWithValue(sizeof(buf))); - ASSERT_THAT(RetryEINTR(write)(sock_.get(), buf, 0), - SyscallSucceedsWithValue(0)); - - struct pollfd pfd = {bind_.get(), POLLIN, 0}; - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - // There should be no control messages. - char recv_buf[sizeof(buf)]; - ASSERT_NO_FATAL_FAILURE(RecvNoCmsg(bind_.get(), recv_buf, sizeof(recv_buf))); - - // A nonzero timeval should be available via ioctl. - struct timeval tv = {}; - ASSERT_THAT(ioctl(bind_.get(), SIOCGSTAMP, &tv), SyscallSucceeds()); - ASSERT_TRUE(tv.tv_sec != 0 || tv.tv_usec != 0); - - // Enable SO_TIMESTAMP and send a message. - int v = 1; - EXPECT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_TIMESTAMP, &v, sizeof(v)), - SyscallSucceeds()); - ASSERT_THAT(RetryEINTR(write)(sock_.get(), buf, 0), - SyscallSucceedsWithValue(0)); - - ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000), - SyscallSucceedsWithValue(1)); - - // There should be a message for SO_TIMESTAMP. - char cmsgbuf[CMSG_SPACE(sizeof(struct timeval))]; - msghdr msg = {}; - iovec iov = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - ASSERT_THAT(RetryEINTR(recvmsg)(bind_.get(), &msg, 0), - SyscallSucceedsWithValue(0)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - - // The ioctl should return the exact same values as before. - struct timeval tv2 = {}; - ASSERT_THAT(ioctl(bind_.get(), SIOCGSTAMP, &tv2), SyscallSucceeds()); - ASSERT_EQ(tv.tv_sec, tv2.tv_sec); - ASSERT_EQ(tv.tv_usec, tv2.tv_usec); -} - -// Test that a socket with IP_TOS or IPV6_TCLASS set will set the TOS byte on -// outgoing packets, and that a receiving socket with IP_RECVTOS or -// IPV6_RECVTCLASS will create the corresponding control message. -TEST_P(UdpSocketTest, SetAndReceiveTOS) { - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Allow socket to receive control message. - int recv_level = SOL_IP; - int recv_type = IP_RECVTOS; - if (GetParam() != AddressFamily::kIpv4) { - recv_level = SOL_IPV6; - recv_type = IPV6_RECVTCLASS; - } - ASSERT_THAT(setsockopt(bind_.get(), recv_level, recv_type, &kSockOptOn, - sizeof(kSockOptOn)), - SyscallSucceeds()); - - // Set socket TOS. - int sent_level = recv_level; - int sent_type = IP_TOS; - if (sent_level == SOL_IPV6) { - sent_type = IPV6_TCLASS; - } - int sent_tos = IPTOS_LOWDELAY; // Choose some TOS value. - ASSERT_THAT(setsockopt(sock_.get(), sent_level, sent_type, &sent_tos, - sizeof(sent_tos)), - SyscallSucceeds()); - - // Prepare message to send. - constexpr size_t kDataLength = 1024; - struct msghdr sent_msg = {}; - struct iovec sent_iov = {}; - char sent_data[kDataLength]; - sent_iov.iov_base = &sent_data[0]; - sent_iov.iov_len = kDataLength; - sent_msg.msg_iov = &sent_iov; - sent_msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(sock_.get(), &sent_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - // Receive message. - struct msghdr received_msg = {}; - struct iovec received_iov = {}; - char received_data[kDataLength]; - received_iov.iov_base = &received_data[0]; - received_iov.iov_len = kDataLength; - received_msg.msg_iov = &received_iov; - received_msg.msg_iovlen = 1; - size_t cmsg_data_len = sizeof(int8_t); - if (sent_type == IPV6_TCLASS) { - cmsg_data_len = sizeof(int); - } - std::vector<char> received_cmsgbuf(CMSG_SPACE(cmsg_data_len)); - received_msg.msg_control = &received_cmsgbuf[0]; - received_msg.msg_controllen = received_cmsgbuf.size(); - ASSERT_THAT(RetryEINTR(recvmsg)(bind_.get(), &received_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len)); - EXPECT_EQ(cmsg->cmsg_level, sent_level); - EXPECT_EQ(cmsg->cmsg_type, sent_type); - int8_t received_tos = 0; - memcpy(&received_tos, CMSG_DATA(cmsg), sizeof(received_tos)); - EXPECT_EQ(received_tos, sent_tos); -} - -// Test that sendmsg with IP_TOS and IPV6_TCLASS control messages will set the -// TOS byte on outgoing packets, and that a receiving socket with IP_RECVTOS or -// IPV6_RECVTCLASS will create the corresponding control message. -TEST_P(UdpSocketTest, SendAndReceiveTOS) { - // TODO(b/146661005): Setting TOS via cmsg not supported for netstack. - SKIP_IF(IsRunningOnGvisor() && !IsRunningWithHostinet()); - - ASSERT_NO_ERRNO(BindLoopback()); - ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); - - // Allow socket to receive control message. - int recv_level = SOL_IP; - int recv_type = IP_RECVTOS; - if (GetParam() != AddressFamily::kIpv4) { - recv_level = SOL_IPV6; - recv_type = IPV6_RECVTCLASS; - } - int recv_opt = kSockOptOn; - ASSERT_THAT(setsockopt(bind_.get(), recv_level, recv_type, &recv_opt, - sizeof(recv_opt)), - SyscallSucceeds()); - - // Prepare message to send. - constexpr size_t kDataLength = 1024; - int sent_level = recv_level; - int sent_type = IP_TOS; - int sent_tos = IPTOS_LOWDELAY; // Choose some TOS value. - - struct msghdr sent_msg = {}; - struct iovec sent_iov = {}; - char sent_data[kDataLength]; - sent_iov.iov_base = &sent_data[0]; - sent_iov.iov_len = kDataLength; - sent_msg.msg_iov = &sent_iov; - sent_msg.msg_iovlen = 1; - size_t cmsg_data_len = sizeof(int8_t); - if (sent_level == SOL_IPV6) { - sent_type = IPV6_TCLASS; - cmsg_data_len = sizeof(int); - } - std::vector<char> sent_cmsgbuf(CMSG_SPACE(cmsg_data_len)); - sent_msg.msg_control = &sent_cmsgbuf[0]; - sent_msg.msg_controllen = CMSG_LEN(cmsg_data_len); - - // Manually add control message. - struct cmsghdr* sent_cmsg = CMSG_FIRSTHDR(&sent_msg); - sent_cmsg->cmsg_len = CMSG_LEN(cmsg_data_len); - sent_cmsg->cmsg_level = sent_level; - sent_cmsg->cmsg_type = sent_type; - *(int8_t*)CMSG_DATA(sent_cmsg) = sent_tos; - - ASSERT_THAT(RetryEINTR(sendmsg)(sock_.get(), &sent_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - // Receive message. - struct msghdr received_msg = {}; - struct iovec received_iov = {}; - char received_data[kDataLength]; - received_iov.iov_base = &received_data[0]; - received_iov.iov_len = kDataLength; - received_msg.msg_iov = &received_iov; - received_msg.msg_iovlen = 1; - std::vector<char> received_cmsgbuf(CMSG_SPACE(cmsg_data_len)); - received_msg.msg_control = &received_cmsgbuf[0]; - received_msg.msg_controllen = CMSG_LEN(cmsg_data_len); - ASSERT_THAT(RetryEINTR(recvmsg)(bind_.get(), &received_msg, 0), - SyscallSucceedsWithValue(kDataLength)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg); - ASSERT_NE(cmsg, nullptr); - EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len)); - EXPECT_EQ(cmsg->cmsg_level, sent_level); - EXPECT_EQ(cmsg->cmsg_type, sent_type); - int8_t received_tos = 0; - memcpy(&received_tos, CMSG_DATA(cmsg), sizeof(received_tos)); - EXPECT_EQ(received_tos, sent_tos); -} - -TEST_P(UdpSocketTest, RecvBufLimitsEmptyRcvBuf) { - // Discover minimum buffer size by setting it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, - sizeof(kRcvBufSz)), - SyscallSucceeds()); - - int min = 0; - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - - // Bind bind_ to loopback. - ASSERT_NO_ERRNO(BindLoopback()); - - { - // Send data of size min and verify that it's received. - std::vector<char> buf(min); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - std::vector<char> received(buf.size()); - EXPECT_THAT(RecvTimeout(bind_.get(), received.data(), received.size(), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(received.size())); - } - - { - // Send data of size min + 1 and verify that its received. Both linux and - // Netstack accept a dgram that exceeds rcvBuf limits if the receive buffer - // is currently empty. - std::vector<char> buf(min + 1); - RandomizeBuffer(buf.data(), buf.size()); - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - - std::vector<char> received(buf.size()); - ASSERT_THAT(RecvTimeout(bind_.get(), received.data(), received.size(), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(received.size())); - } -} - -// Test that receive buffer limits are enforced. -TEST_P(UdpSocketTest, RecvBufLimits) { - // Bind s_ to loopback. - ASSERT_NO_ERRNO(BindLoopback()); - - int min = 0; - { - // Discover minimum buffer size by trying to set it to zero. - constexpr int kRcvBufSz = 0; - ASSERT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz, - sizeof(kRcvBufSz)), - SyscallSucceeds()); - - socklen_t min_len = sizeof(min); - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_RCVBUF, &min, &min_len), - SyscallSucceeds()); - } - - // Now set the limit to min * 4. - int new_rcv_buf_sz = min * 4; - if (!IsRunningOnGvisor() || IsRunningWithHostinet()) { - // Linux doubles the value specified so just set to min * 2. - new_rcv_buf_sz = min * 2; - } - - ASSERT_THAT(setsockopt(bind_.get(), SOL_SOCKET, SO_RCVBUF, &new_rcv_buf_sz, - sizeof(new_rcv_buf_sz)), - SyscallSucceeds()); - int rcv_buf_sz = 0; - { - socklen_t rcv_buf_len = sizeof(rcv_buf_sz); - ASSERT_THAT(getsockopt(bind_.get(), SOL_SOCKET, SO_RCVBUF, &rcv_buf_sz, - &rcv_buf_len), - SyscallSucceeds()); - } - - { - std::vector<char> buf(min); - RandomizeBuffer(buf.data(), buf.size()); - - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - int sent = 4; - if (IsRunningOnGvisor() && !IsRunningWithHostinet()) { - // Linux seems to drop the 4th packet even though technically it should - // fit in the receive buffer. - ASSERT_THAT( - sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), - SyscallSucceedsWithValue(buf.size())); - sent++; - } - - for (int i = 0; i < sent - 1; i++) { - // Receive the data. - std::vector<char> received(buf.size()); - EXPECT_THAT(RecvTimeout(bind_.get(), received.data(), received.size(), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(received.size())); - EXPECT_EQ(memcmp(buf.data(), received.data(), buf.size()), 0); - } - - // The last receive should fail with EAGAIN as the last packet should have - // been dropped due to lack of space in the receive buffer. - std::vector<char> received(buf.size()); - EXPECT_THAT( - recv(bind_.get(), received.data(), received.size(), MSG_DONTWAIT), - SyscallFailsWithErrno(EAGAIN)); - } -} - -#ifdef __linux__ - -// TODO(gvisor.dev/2746): Support SO_ATTACH_FILTER/SO_DETACH_FILTER. -// gVisor currently silently ignores attaching a filter. -TEST_P(UdpSocketTest, SetSocketDetachFilter) { - // Program generated using sudo tcpdump -i lo udp and port 1234 -dd - struct sock_filter code[] = { - {0x28, 0, 0, 0x0000000c}, {0x15, 0, 6, 0x000086dd}, - {0x30, 0, 0, 0x00000014}, {0x15, 0, 15, 0x00000011}, - {0x28, 0, 0, 0x00000036}, {0x15, 12, 0, 0x000004d2}, - {0x28, 0, 0, 0x00000038}, {0x15, 10, 11, 0x000004d2}, - {0x15, 0, 10, 0x00000800}, {0x30, 0, 0, 0x00000017}, - {0x15, 0, 8, 0x00000011}, {0x28, 0, 0, 0x00000014}, - {0x45, 6, 0, 0x00001fff}, {0xb1, 0, 0, 0x0000000e}, - {0x48, 0, 0, 0x0000000e}, {0x15, 2, 0, 0x000004d2}, - {0x48, 0, 0, 0x00000010}, {0x15, 0, 1, 0x000004d2}, - {0x6, 0, 0, 0x00040000}, {0x6, 0, 0, 0x00000000}, - }; - struct sock_fprog bpf = { - .len = ABSL_ARRAYSIZE(code), - .filter = code, - }; - ASSERT_THAT( - setsockopt(sock_.get(), SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf)), - SyscallSucceeds()); - - constexpr int val = 0; - ASSERT_THAT( - setsockopt(sock_.get(), SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallSucceeds()); -} - -#endif // __linux__ - -TEST_P(UdpSocketTest, SetSocketDetachFilterNoInstalledFilter) { - // TODO(gvisor.dev/2746): Support SO_ATTACH_FILTER/SO_DETACH_FILTER. - SKIP_IF(IsRunningOnGvisor()); - constexpr int val = 0; - ASSERT_THAT( - setsockopt(sock_.get(), SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)), - SyscallFailsWithErrno(ENOENT)); -} - -TEST_P(UdpSocketTest, GetSocketDetachFilter) { - int val = 0; - socklen_t val_len = sizeof(val); - ASSERT_THAT( - getsockopt(sock_.get(), SOL_SOCKET, SO_DETACH_FILTER, &val, &val_len), - SyscallFailsWithErrno(ENOPROTOOPT)); -} - -TEST_P(UdpSocketTest, SendToZeroPort) { - char buf[8]; - struct sockaddr_storage addr = InetLoopbackAddr(); - - // Sending to an invalid port should fail. - SetPort(&addr, 0); - EXPECT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, - reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallFailsWithErrno(EINVAL)); - - SetPort(&addr, 1234); - EXPECT_THAT(sendto(sock_.get(), buf, sizeof(buf), 0, - reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)), - SyscallSucceedsWithValue(sizeof(buf))); -} - -TEST_P(UdpSocketTest, ConnectToZeroPortUnbound) { - struct sockaddr_storage addr = InetLoopbackAddr(); - SetPort(&addr, 0); - ASSERT_THAT( - connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_), - SyscallSucceeds()); -} - -TEST_P(UdpSocketTest, ConnectToZeroPortBound) { - struct sockaddr_storage addr = InetLoopbackAddr(); - ASSERT_NO_ERRNO( - BindSocket(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr))); - - SetPort(&addr, 0); - ASSERT_THAT( - connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_), - SyscallSucceeds()); - socklen_t len = sizeof(sockaddr_storage); - ASSERT_THAT( - getsockname(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), &len), - SyscallSucceeds()); - ASSERT_EQ(len, addrlen_); -} - -TEST_P(UdpSocketTest, ConnectToZeroPortConnected) { - struct sockaddr_storage addr = InetLoopbackAddr(); - ASSERT_NO_ERRNO( - BindSocket(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr))); - - // Connect to an address with non-zero port should succeed. - ASSERT_THAT( - connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_), - SyscallSucceeds()); - sockaddr_storage peername; - socklen_t peerlen = sizeof(peername); - ASSERT_THAT( - getpeername(sock_.get(), reinterpret_cast<struct sockaddr*>(&peername), - &peerlen), - SyscallSucceeds()); - ASSERT_EQ(peerlen, addrlen_); - ASSERT_EQ(memcmp(&peername, &addr, addrlen_), 0); - - // However connect() to an address with port 0 will make the following - // getpeername() fail. - SetPort(&addr, 0); - ASSERT_THAT( - connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_), - SyscallSucceeds()); - ASSERT_THAT( - getpeername(sock_.get(), reinterpret_cast<struct sockaddr*>(&peername), - &peerlen), - SyscallFailsWithErrno(ENOTCONN)); -} - -INSTANTIATE_TEST_SUITE_P(AllInetTests, UdpSocketTest, - ::testing::Values(AddressFamily::kIpv4, - AddressFamily::kIpv6, - AddressFamily::kDualStack)); - -TEST(UdpInet6SocketTest, ConnectInet4Sockaddr) { - // glibc getaddrinfo expects the invariant expressed by this test to be held. - const sockaddr_in connect_sockaddr = { - .sin_family = AF_INET, .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}}; - auto sock_ = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)); - ASSERT_THAT( - connect(sock_.get(), - reinterpret_cast<const struct sockaddr*>(&connect_sockaddr), - sizeof(sockaddr_in)), - SyscallSucceeds()); - sockaddr_storage sockname; - socklen_t len = sizeof(sockaddr_storage); - ASSERT_THAT(getsockname(sock_.get(), - reinterpret_cast<struct sockaddr*>(&sockname), &len), - SyscallSucceeds()); - ASSERT_EQ(sockname.ss_family, AF_INET6); - ASSERT_EQ(len, sizeof(sockaddr_in6)); - auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&sockname); - char addr_buf[INET6_ADDRSTRLEN]; - const char* addr; - ASSERT_NE(addr = inet_ntop(sockname.ss_family, &sockname, addr_buf, - sizeof(addr_buf)), - nullptr); - ASSERT_TRUE(IN6_IS_ADDR_V4MAPPED(sin6->sin6_addr.s6_addr)) << addr; -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc deleted file mode 100644 index 4139a18d8..000000000 --- a/test/syscalls/linux/uidgid.cc +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <grp.h> -#include <sys/resource.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "test/util/capability_util.h" -#include "test/util/cleanup.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/uid_util.h" - -ABSL_FLAG(int32_t, scratch_uid1, 65534, "first scratch UID"); -ABSL_FLAG(int32_t, scratch_uid2, 65533, "second scratch UID"); -ABSL_FLAG(int32_t, scratch_gid1, 65534, "first scratch GID"); -ABSL_FLAG(int32_t, scratch_gid2, 65533, "second scratch GID"); - -// Force use of syscall instead of glibc set*id() wrappers because we want to -// apply to the current task only. libc sets all threads in a process because -// "POSIX requires that all threads in a process share the same credentials." -#define setuid USE_SYSCALL_INSTEAD -#define setgid USE_SYSCALL_INSTEAD -#define setreuid USE_SYSCALL_INSTEAD -#define setregid USE_SYSCALL_INSTEAD -#define setresuid USE_SYSCALL_INSTEAD -#define setresgid USE_SYSCALL_INSTEAD - -using ::testing::UnorderedElementsAreArray; - -namespace gvisor { -namespace testing { - -namespace { - -TEST(UidGidTest, Getuid) { - uid_t ruid, euid, suid; - EXPECT_THAT(getresuid(&ruid, &euid, &suid), SyscallSucceeds()); - EXPECT_THAT(getuid(), SyscallSucceedsWithValue(ruid)); - EXPECT_THAT(geteuid(), SyscallSucceedsWithValue(euid)); -} - -TEST(UidGidTest, Getgid) { - gid_t rgid, egid, sgid; - EXPECT_THAT(getresgid(&rgid, &egid, &sgid), SyscallSucceeds()); - EXPECT_THAT(getgid(), SyscallSucceedsWithValue(rgid)); - EXPECT_THAT(getegid(), SyscallSucceedsWithValue(egid)); -} - -TEST(UidGidTest, Getgroups) { - // "If size is zero, list is not modified, but the total number of - // supplementary group IDs for the process is returned." - getgroups(2) - int nr_groups; - ASSERT_THAT(nr_groups = getgroups(0, nullptr), SyscallSucceeds()); - std::vector<gid_t> list(nr_groups); - EXPECT_THAT(getgroups(list.size(), list.data()), SyscallSucceeds()); - - // "EINVAL: size is less than the number of supplementary group IDs, but is - // not zero." - EXPECT_THAT(getgroups(-1, nullptr), SyscallFailsWithErrno(EINVAL)); - - // Testing for EFAULT requires actually having groups, which isn't guaranteed - // here; see the setgroups test below. -} - -// Checks that the calling process' real/effective/saved user IDs are -// ruid/euid/suid respectively. -PosixError CheckUIDs(uid_t ruid, uid_t euid, uid_t suid) { - uid_t actual_ruid, actual_euid, actual_suid; - int rc = getresuid(&actual_ruid, &actual_euid, &actual_suid); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "getresuid"); - } - if (ruid != actual_ruid || euid != actual_euid || suid != actual_suid) { - return PosixError( - EPERM, absl::StrCat( - "incorrect user IDs: got (", - absl::StrJoin({actual_ruid, actual_euid, actual_suid}, ", "), - ", wanted (", absl::StrJoin({ruid, euid, suid}, ", "), ")")); - } - return NoError(); -} - -PosixError CheckGIDs(gid_t rgid, gid_t egid, gid_t sgid) { - gid_t actual_rgid, actual_egid, actual_sgid; - int rc = getresgid(&actual_rgid, &actual_egid, &actual_sgid); - MaybeSave(); - if (rc < 0) { - return PosixError(errno, "getresgid"); - } - if (rgid != actual_rgid || egid != actual_egid || sgid != actual_sgid) { - return PosixError( - EPERM, absl::StrCat( - "incorrect group IDs: got (", - absl::StrJoin({actual_rgid, actual_egid, actual_sgid}, ", "), - ", wanted (", absl::StrJoin({rgid, egid, sgid}, ", "), ")")); - } - return NoError(); -} - -// N.B. These tests may break horribly unless run via a gVisor test runner, -// because changing UID in one test may forfeit permissions required by other -// tests. (The test runner runs each test in a separate process.) - -TEST(UidGidRootTest, Setuid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting this - // test. Otherwise, the files are created by root (UID before the test), but - // cannot be opened by the `uid` set below after the test. After calling - // setuid(non-zero-UID), there is no way to get root privileges back. - ScopedThread([&] { - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. POSIX threads, however, require that all - // threads have the same UIDs, so using the setuid wrapper sets all threads' - // real UID. - EXPECT_THAT(syscall(SYS_setuid, -1), SyscallFailsWithErrno(EINVAL)); - - const uid_t uid = absl::GetFlag(FLAGS_scratch_uid1); - EXPECT_THAT(syscall(SYS_setuid, uid), SyscallSucceeds()); - // "If the effective UID of the caller is root (more precisely: if the - // caller has the CAP_SETUID capability), the real UID and saved set-user-ID - // are also set." - setuid(2) - EXPECT_NO_ERRNO(CheckUIDs(uid, uid, uid)); - }); -} - -TEST(UidGidRootTest, Setgid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - EXPECT_THAT(syscall(SYS_setgid, -1), SyscallFailsWithErrno(EINVAL)); - - ScopedThread([&] { - const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1); - EXPECT_THAT(syscall(SYS_setgid, gid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); - }); -} - -TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) { -#pragma push_macro("allow_setgid") -#undef setgid - - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - int old_gid = getgid(); - auto clean = Cleanup([old_gid] { setgid(old_gid); }); - - const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1); - // NOTE(b/64676707): Do setgid in a separate thread so that we can test if - // info.si_pid is set correctly. - ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); }); - EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid)); - -#pragma pop_macro("allow_setgid") -} - -TEST(UidGidRootTest, Setreuid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // "Supplying a value of -1 for either the real or effective user ID forces - // the system to leave that ID unchanged." - setreuid(2) - EXPECT_THAT(syscall(SYS_setreuid, -1, -1), SyscallSucceeds()); - - EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. After - // calling setuid(non-zero-UID), there is no way to get root privileges - // back. - ScopedThread([&] { - const uid_t ruid = absl::GetFlag(FLAGS_scratch_uid1); - const uid_t euid = absl::GetFlag(FLAGS_scratch_uid2); - - EXPECT_THAT(syscall(SYS_setreuid, ruid, euid), SyscallSucceeds()); - - // "If the real user ID is set or the effective user ID is set to a value - // not equal to the previous real user ID, the saved set-user-ID will be - // set to the new effective user ID." - setreuid(2) - EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, euid)); - }); -} - -TEST(UidGidRootTest, Setregid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - EXPECT_THAT(syscall(SYS_setregid, -1, -1), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); - - ScopedThread([&] { - const gid_t rgid = absl::GetFlag(FLAGS_scratch_gid1); - const gid_t egid = absl::GetFlag(FLAGS_scratch_gid2); - ASSERT_THAT(syscall(SYS_setregid, rgid, egid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, egid)); - }); -} - -TEST(UidGidRootTest, Setresuid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // "If one of the arguments equals -1, the corresponding value is not - // changed." - setresuid(2) - EXPECT_THAT(syscall(SYS_setresuid, -1, -1, -1), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckUIDs(0, 0, 0)); - - // Do setuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. After - // calling setuid(non-zero-UID), there is no way to get root privileges - // back. - ScopedThread([&] { - const uid_t ruid = 12345; - const uid_t euid = 23456; - const uid_t suid = 34567; - - // Use syscall instead of glibc setuid wrapper because we want this setuid - // call to only apply to this task. posix threads, however, require that - // all threads have the same UIDs, so using the setuid wrapper sets all - // threads' real UID. - EXPECT_THAT(syscall(SYS_setresuid, ruid, euid, suid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckUIDs(ruid, euid, suid)); - }); -} - -TEST(UidGidRootTest, Setresgid) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - EXPECT_THAT(syscall(SYS_setresgid, -1, -1, -1), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(0, 0, 0)); - - ScopedThread([&] { - const gid_t rgid = 12345; - const gid_t egid = 23456; - const gid_t sgid = 34567; - ASSERT_THAT(syscall(SYS_setresgid, rgid, egid, sgid), SyscallSucceeds()); - EXPECT_NO_ERRNO(CheckGIDs(rgid, egid, sgid)); - }); -} - -TEST(UidGidRootTest, Setgroups) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - std::vector<gid_t> list = {123, 500}; - ASSERT_THAT(setgroups(list.size(), list.data()), SyscallSucceeds()); - std::vector<gid_t> list2(list.size()); - ASSERT_THAT(getgroups(list2.size(), list2.data()), SyscallSucceeds()); - EXPECT_THAT(list, UnorderedElementsAreArray(list2)); - - // "EFAULT: list has an invalid address." - EXPECT_THAT(getgroups(100, reinterpret_cast<gid_t*>(-1)), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(UidGidRootTest, Setuid_prlimit) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsRoot())); - - // Do seteuid in a separate thread so that after finishing this test, the - // process can still open files the test harness created before starting - // this test. Otherwise, the files are created by root (UID before the - // test), but cannot be opened by the `uid` set below after the test. - ScopedThread([&] { - // Use syscall instead of glibc setuid wrapper because we want this - // seteuid call to only apply to this task. POSIX threads, however, - // require that all threads have the same UIDs, so using the seteuid - // wrapper sets all threads' UID. - EXPECT_THAT(syscall(SYS_setreuid, -1, 65534), SyscallSucceeds()); - - // Despite the UID change, we should be able to get our own limits. - struct rlimit rl = {}; - EXPECT_THAT(prlimit(0, RLIMIT_NOFILE, NULL, &rl), SyscallSucceeds()); - }); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/uname.cc b/test/syscalls/linux/uname.cc deleted file mode 100644 index d8824b171..000000000 --- a/test/syscalls/linux/uname.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <sched.h> -#include <sys/utsname.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/strings/string_view.h" -#include "test/util/capability_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(UnameTest, Sanity) { - struct utsname buf; - ASSERT_THAT(uname(&buf), SyscallSucceeds()); - EXPECT_NE(strlen(buf.release), 0); - EXPECT_NE(strlen(buf.version), 0); - EXPECT_NE(strlen(buf.machine), 0); - EXPECT_NE(strlen(buf.sysname), 0); - EXPECT_NE(strlen(buf.nodename), 0); - EXPECT_NE(strlen(buf.domainname), 0); -} - -TEST(UnameTest, SetNames) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - char hostname[65]; - ASSERT_THAT(sethostname("0123456789", 3), SyscallSucceeds()); - EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(hostname), "012"); - - ASSERT_THAT(sethostname("0123456789\0xxx", 11), SyscallSucceeds()); - EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(hostname), "0123456789"); - - ASSERT_THAT(sethostname("0123456789\0xxx", 12), SyscallSucceeds()); - EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(hostname), "0123456789"); - - constexpr char kHostname[] = "wubbalubba"; - ASSERT_THAT(sethostname(kHostname, sizeof(kHostname)), SyscallSucceeds()); - - constexpr char kDomainname[] = "dubdub.com"; - ASSERT_THAT(setdomainname(kDomainname, sizeof(kDomainname)), - SyscallSucceeds()); - - struct utsname buf; - EXPECT_THAT(uname(&buf), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(buf.nodename), kHostname); - EXPECT_EQ(absl::string_view(buf.domainname), kDomainname); - - // These should just be glibc wrappers that also call uname(2). - EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(hostname), kHostname); - - char domainname[65]; - EXPECT_THAT(getdomainname(domainname, sizeof(domainname)), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(domainname), kDomainname); -} - -TEST(UnameTest, UnprivilegedSetNames) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); - } - - EXPECT_THAT(sethostname("", 0), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(setdomainname("", 0), SyscallFailsWithErrno(EPERM)); -} - -TEST(UnameTest, UnshareUTS) { - SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - struct utsname init; - ASSERT_THAT(uname(&init), SyscallSucceeds()); - - ScopedThread([&]() { - EXPECT_THAT(unshare(CLONE_NEWUTS), SyscallSucceeds()); - - constexpr char kHostname[] = "wubbalubba"; - EXPECT_THAT(sethostname(kHostname, sizeof(kHostname)), SyscallSucceeds()); - - char hostname[65]; - EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds()); - }); - - struct utsname after; - EXPECT_THAT(uname(&after), SyscallSucceeds()); - EXPECT_EQ(absl::string_view(after.nodename), init.nodename); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/unix_domain_socket_test_util.cc b/test/syscalls/linux/unix_domain_socket_test_util.cc deleted file mode 100644 index b05ab2900..000000000 --- a/test/syscalls/linux/unix_domain_socket_test_util.cc +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "test/syscalls/linux/unix_domain_socket_test_util.h" - -#include <sys/un.h> - -#include <vector> - -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -std::string DescribeUnixDomainSocketType(int type) { - const char* type_str = nullptr; - switch (type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) { - case SOCK_STREAM: - type_str = "SOCK_STREAM"; - break; - case SOCK_DGRAM: - type_str = "SOCK_DGRAM"; - break; - case SOCK_SEQPACKET: - type_str = "SOCK_SEQPACKET"; - break; - } - if (!type_str) { - return absl::StrCat("Unix domain socket with unknown type ", type); - } else { - return absl::StrCat(((type & SOCK_NONBLOCK) != 0) ? "non-blocking " : "", - ((type & SOCK_CLOEXEC) != 0) ? "close-on-exec " : "", - type_str, " Unix domain socket"); - } -} - -SocketPairKind UnixDomainSocketPair(int type) { - return SocketPairKind{DescribeUnixDomainSocketType(type), AF_UNIX, type, 0, - SyscallSocketPairCreator(AF_UNIX, type, 0)}; -} - -SocketPairKind FilesystemBoundUnixDomainSocketPair(int type) { - std::string description = absl::StrCat(DescribeUnixDomainSocketType(type), - " created with filesystem binding"); - if ((type & SOCK_DGRAM) == SOCK_DGRAM) { - return SocketPairKind{ - description, AF_UNIX, type, 0, - FilesystemBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)}; - } - return SocketPairKind{ - description, AF_UNIX, type, 0, - FilesystemAcceptBindSocketPairCreator(AF_UNIX, type, 0)}; -} - -SocketPairKind AbstractBoundUnixDomainSocketPair(int type) { - std::string description = - absl::StrCat(DescribeUnixDomainSocketType(type), - " created with abstract namespace binding"); - if ((type & SOCK_DGRAM) == SOCK_DGRAM) { - return SocketPairKind{ - description, AF_UNIX, type, 0, - AbstractBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)}; - } - return SocketPairKind{description, AF_UNIX, type, 0, - AbstractAcceptBindSocketPairCreator(AF_UNIX, type, 0)}; -} - -SocketPairKind SocketpairGoferUnixDomainSocketPair(int type) { - std::string description = absl::StrCat(DescribeUnixDomainSocketType(type), - " created with the socketpair gofer"); - return SocketPairKind{description, AF_UNIX, type, 0, - SocketpairGoferSocketPairCreator(AF_UNIX, type, 0)}; -} - -SocketPairKind SocketpairGoferFileSocketPair(int type) { - std::string description = - absl::StrCat(((type & O_NONBLOCK) != 0) ? "non-blocking " : "", - ((type & O_CLOEXEC) != 0) ? "close-on-exec " : "", - "file socket created with the socketpair gofer"); - // The socketpair gofer always creates SOCK_STREAM sockets on open(2). - return SocketPairKind{description, AF_UNIX, SOCK_STREAM, 0, - SocketpairGoferFileSocketPairCreator(type)}; -} - -SocketPairKind FilesystemUnboundUnixDomainSocketPair(int type) { - return SocketPairKind{absl::StrCat(DescribeUnixDomainSocketType(type), - " unbound with a filesystem address"), - AF_UNIX, type, 0, - FilesystemUnboundSocketPairCreator(AF_UNIX, type, 0)}; -} - -SocketPairKind AbstractUnboundUnixDomainSocketPair(int type) { - return SocketPairKind{ - absl::StrCat(DescribeUnixDomainSocketType(type), - " unbound with an abstract namespace address"), - AF_UNIX, type, 0, AbstractUnboundSocketPairCreator(AF_UNIX, type, 0)}; -} - -void SendSingleFD(int sock, int fd, char buf[], int buf_size) { - ASSERT_NO_FATAL_FAILURE(SendFDs(sock, &fd, 1, buf, buf_size)); -} - -void SendFDs(int sock, int fds[], int fds_size, char buf[], int buf_size) { - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(fds_size * sizeof(int))); - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(fds_size * sizeof(int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - for (int i = 0; i < fds_size; i++) { - memcpy(CMSG_DATA(cmsg) + i * sizeof(int), &fds[i], sizeof(int)); - } - - ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), - IsPosixErrorOkAndHolds(buf_size)); -} - -void RecvSingleFD(int sock, int* fd, char buf[], int buf_size) { - ASSERT_NO_FATAL_FAILURE(RecvFDs(sock, fd, 1, buf, buf_size, buf_size)); -} - -void RecvSingleFD(int sock, int* fd, char buf[], int buf_size, - int expected_size) { - ASSERT_NO_FATAL_FAILURE(RecvFDs(sock, fd, 1, buf, buf_size, expected_size)); -} - -void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size) { - ASSERT_NO_FATAL_FAILURE( - RecvFDs(sock, fds, fds_size, buf, buf_size, buf_size)); -} - -void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size, - int expected_size, bool peek) { - struct msghdr msg = {}; - std::vector<char> control(CMSG_SPACE(fds_size * sizeof(int))); - msg.msg_control = &control[0]; - msg.msg_controllen = control.size(); - - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = buf_size; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - int flags = 0; - if (peek) { - flags |= MSG_PEEK; - } - - ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, flags), - SyscallSucceedsWithValue(expected_size)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(fds_size * sizeof(int))); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); - - for (int i = 0; i < fds_size; i++) { - memcpy(&fds[i], CMSG_DATA(cmsg) + i * sizeof(int), sizeof(int)); - } -} - -void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size, - int expected_size) { - ASSERT_NO_FATAL_FAILURE( - RecvFDs(sock, fds, fds_size, buf, buf_size, expected_size, false)); -} - -void PeekSingleFD(int sock, int* fd, char buf[], int buf_size) { - ASSERT_NO_FATAL_FAILURE(RecvFDs(sock, fd, 1, buf, buf_size, buf_size, true)); -} - -void RecvNoCmsg(int sock, char buf[], int buf_size, int expected_size) { - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = buf_size; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), - SyscallSucceedsWithValue(expected_size)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - EXPECT_EQ(cmsg, nullptr); -} - -void SendNullCmsg(int sock, char buf[], int buf_size) { - struct msghdr msg = {}; - msg.msg_control = nullptr; - msg.msg_controllen = 0; - - ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), - IsPosixErrorOkAndHolds(buf_size)); -} - -void SendCreds(int sock, ucred creds, char buf[], int buf_size) { - struct msghdr msg = {}; - - char control[CMSG_SPACE(sizeof(struct ucred))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_CREDENTIALS; - cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - memcpy(CMSG_DATA(cmsg), &creds, sizeof(struct ucred)); - - ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), - IsPosixErrorOkAndHolds(buf_size)); -} - -void SendCredsAndFD(int sock, ucred creds, int fd, char buf[], int buf_size) { - struct msghdr msg = {}; - - char control[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))] = {}; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct cmsghdr* cmsg1 = CMSG_FIRSTHDR(&msg); - cmsg1->cmsg_level = SOL_SOCKET; - cmsg1->cmsg_type = SCM_CREDENTIALS; - cmsg1->cmsg_len = CMSG_LEN(sizeof(struct ucred)); - memcpy(CMSG_DATA(cmsg1), &creds, sizeof(struct ucred)); - - struct cmsghdr* cmsg2 = CMSG_NXTHDR(&msg, cmsg1); - cmsg2->cmsg_level = SOL_SOCKET; - cmsg2->cmsg_type = SCM_RIGHTS; - cmsg2->cmsg_len = CMSG_LEN(sizeof(int)); - memcpy(CMSG_DATA(cmsg2), &fd, sizeof(int)); - - ASSERT_THAT(SendMsg(sock, &msg, buf, buf_size), - IsPosixErrorOkAndHolds(buf_size)); -} - -void RecvCreds(int sock, ucred* creds, char buf[], int buf_size) { - ASSERT_NO_FATAL_FAILURE(RecvCreds(sock, creds, buf, buf_size, buf_size)); -} - -void RecvCreds(int sock, ucred* creds, char buf[], int buf_size, - int expected_size) { - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(struct ucred))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = buf_size; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), - SyscallSucceedsWithValue(expected_size)); - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(struct ucred))); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS); - - memcpy(creds, CMSG_DATA(cmsg), sizeof(struct ucred)); -} - -void RecvCredsAndFD(int sock, ucred* creds, int* fd, char buf[], int buf_size) { - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(int))]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = buf_size; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), - SyscallSucceedsWithValue(buf_size)); - - struct cmsghdr* cmsg1 = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg1, nullptr); - ASSERT_EQ(cmsg1->cmsg_len, CMSG_LEN(sizeof(struct ucred))); - ASSERT_EQ(cmsg1->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg1->cmsg_type, SCM_CREDENTIALS); - memcpy(creds, CMSG_DATA(cmsg1), sizeof(struct ucred)); - - struct cmsghdr* cmsg2 = CMSG_NXTHDR(&msg, cmsg1); - ASSERT_NE(cmsg2, nullptr); - ASSERT_EQ(cmsg2->cmsg_len, CMSG_LEN(sizeof(int))); - ASSERT_EQ(cmsg2->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg2->cmsg_type, SCM_RIGHTS); - memcpy(fd, CMSG_DATA(cmsg2), sizeof(int)); -} - -void RecvSingleFDUnaligned(int sock, int* fd, char buf[], int buf_size) { - struct msghdr msg = {}; - char control[CMSG_SPACE(sizeof(int)) - sizeof(int)]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = buf_size; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(recvmsg)(sock, &msg, 0), - SyscallSucceedsWithValue(buf_size)); - - struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); - ASSERT_NE(cmsg, nullptr); - ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int))); - ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET); - ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS); - - memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); -} - -void SetSoPassCred(int sock) { - int one = 1; - EXPECT_THAT(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)), - SyscallSucceeds()); -} - -void UnsetSoPassCred(int sock) { - int zero = 0; - EXPECT_THAT(setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &zero, sizeof(zero)), - SyscallSucceeds()); -} - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/unix_domain_socket_test_util.h b/test/syscalls/linux/unix_domain_socket_test_util.h deleted file mode 100644 index b8073db17..000000000 --- a/test/syscalls/linux/unix_domain_socket_test_util.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef GVISOR_TEST_SYSCALLS_UNIX_DOMAIN_SOCKET_TEST_UTIL_H_ -#define GVISOR_TEST_SYSCALLS_UNIX_DOMAIN_SOCKET_TEST_UTIL_H_ - -#include <string> - -#include "test/syscalls/linux/socket_test_util.h" - -namespace gvisor { -namespace testing { - -// DescribeUnixDomainSocketType returns a human-readable string explaining the -// given Unix domain socket type. -std::string DescribeUnixDomainSocketType(int type); - -// UnixDomainSocketPair returns a SocketPairKind that represents SocketPairs -// created by invoking the socketpair() syscall with AF_UNIX and the given type. -SocketPairKind UnixDomainSocketPair(int type); - -// FilesystemBoundUnixDomainSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and accept() syscalls with a temp file path, -// AF_UNIX and the given type. -SocketPairKind FilesystemBoundUnixDomainSocketPair(int type); - -// AbstractBoundUnixDomainSocketPair returns a SocketPairKind that represents -// SocketPairs created with bind() and accept() syscalls with a temp abstract -// path, AF_UNIX and the given type. -SocketPairKind AbstractBoundUnixDomainSocketPair(int type); - -// SocketpairGoferUnixDomainSocketPair returns a SocketPairKind that was created -// with two sockets connected to the socketpair gofer. -SocketPairKind SocketpairGoferUnixDomainSocketPair(int type); - -// SocketpairGoferFileSocketPair returns a SocketPairKind that was created with -// two open() calls on paths backed by the socketpair gofer. -SocketPairKind SocketpairGoferFileSocketPair(int type); - -// FilesystemUnboundUnixDomainSocketPair returns a SocketPairKind that -// represents two unbound sockets and a filesystem path for binding. -SocketPairKind FilesystemUnboundUnixDomainSocketPair(int type); - -// AbstractUnboundUnixDomainSocketPair returns a SocketPairKind that represents -// two unbound sockets and an abstract namespace path for binding. -SocketPairKind AbstractUnboundUnixDomainSocketPair(int type); - -// SendSingleFD sends both a single FD and some data over a unix domain socket -// specified by an FD. Note that calls to this function must be wrapped in -// ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void SendSingleFD(int sock, int fd, char buf[], int buf_size); - -// SendFDs sends an arbitrary number of FDs and some data over a unix domain -// socket specified by an FD. Note that calls to this function must be wrapped -// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void SendFDs(int sock, int fds[], int fds_size, char buf[], int buf_size); - -// RecvSingleFD receives both a single FD and some data over a unix domain -// socket specified by an FD. Note that calls to this function must be wrapped -// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void RecvSingleFD(int sock, int* fd, char buf[], int buf_size); - -// RecvSingleFD receives both a single FD and some data over a unix domain -// socket specified by an FD. This version allows the expected amount of data -// received to be different than the buffer size. Note that calls to this -// function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions -// to halt the test. -void RecvSingleFD(int sock, int* fd, char buf[], int buf_size, - int expected_size); - -// PeekSingleFD peeks at both a single FD and some data over a unix domain -// socket specified by an FD. Note that calls to this function must be wrapped -// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void PeekSingleFD(int sock, int* fd, char buf[], int buf_size); - -// RecvFDs receives both an arbitrary number of FDs and some data over a unix -// domain socket specified by an FD. Note that calls to this function must be -// wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size); - -// RecvFDs receives both an arbitrary number of FDs and some data over a unix -// domain socket specified by an FD. This version allows the expected amount of -// data received to be different than the buffer size. Note that calls to this -// function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions -// to halt the test. -void RecvFDs(int sock, int fds[], int fds_size, char buf[], int buf_size, - int expected_size); - -// RecvNoCmsg receives some data over a unix domain socket specified by an FD -// and asserts that no control messages are available for receiving. Note that -// calls to this function must be wrapped in ASSERT_NO_FATAL_FAILURE for -// internal assertions to halt the test. -void RecvNoCmsg(int sock, char buf[], int buf_size, int expected_size); - -inline void RecvNoCmsg(int sock, char buf[], int buf_size) { - RecvNoCmsg(sock, buf, buf_size, buf_size); -} - -// SendCreds sends the credentials of the current process and some data over a -// unix domain socket specified by an FD. Note that calls to this function must -// be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the -// test. -void SendCreds(int sock, ucred creds, char buf[], int buf_size); - -// SendCredsAndFD sends the credentials of the current process, a single FD, and -// some data over a unix domain socket specified by an FD. Note that calls to -// this function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal -// assertions to halt the test. -void SendCredsAndFD(int sock, ucred creds, int fd, char buf[], int buf_size); - -// RecvCreds receives some credentials and some data over a unix domain socket -// specified by an FD. Note that calls to this function must be wrapped in -// ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void RecvCreds(int sock, ucred* creds, char buf[], int buf_size); - -// RecvCreds receives some credentials and some data over a unix domain socket -// specified by an FD. This version allows the expected amount of data received -// to be different than the buffer size. Note that calls to this function must -// be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the -// test. -void RecvCreds(int sock, ucred* creds, char buf[], int buf_size, - int expected_size); - -// RecvCredsAndFD receives some credentials, a single FD, and some data over a -// unix domain socket specified by an FD. Note that calls to this function must -// be wrapped in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the -// test. -void RecvCredsAndFD(int sock, ucred* creds, int* fd, char buf[], int buf_size); - -// SendNullCmsg sends a null control message and some data over a unix domain -// socket specified by an FD. Note that calls to this function must be wrapped -// in ASSERT_NO_FATAL_FAILURE for internal assertions to halt the test. -void SendNullCmsg(int sock, char buf[], int buf_size); - -// RecvSingleFDUnaligned sends both a single FD and some data over a unix domain -// socket specified by an FD. This function does not obey the spec, but Linux -// allows it and the apphosting code depends on this quirk. Note that calls to -// this function must be wrapped in ASSERT_NO_FATAL_FAILURE for internal -// assertions to halt the test. -void RecvSingleFDUnaligned(int sock, int* fd, char buf[], int buf_size); - -// SetSoPassCred sets the SO_PASSCRED option on the specified socket. -void SetSoPassCred(int sock); - -// UnsetSoPassCred clears the SO_PASSCRED option on the specified socket. -void UnsetSoPassCred(int sock); - -} // namespace testing -} // namespace gvisor - -#endif // GVISOR_TEST_SYSCALLS_UNIX_DOMAIN_SOCKET_TEST_UTIL_H_ diff --git a/test/syscalls/linux/unlink.cc b/test/syscalls/linux/unlink.cc deleted file mode 100644 index 061e2e0f1..000000000 --- a/test/syscalls/linux/unlink.cc +++ /dev/null @@ -1,228 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(UnlinkTest, IsDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - EXPECT_THAT(unlink(dir.path().c_str()), SyscallFailsWithErrno(EISDIR)); -} - -TEST(UnlinkTest, DirNotEmpty) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - int fd; - std::string path = JoinPath(dir.path(), "ExistingFile"); - EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - EXPECT_THAT(rmdir(dir.path().c_str()), SyscallFailsWithErrno(ENOTEMPTY)); -} - -TEST(UnlinkTest, Rmdir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - EXPECT_THAT(rmdir(dir.path().c_str()), SyscallSucceeds()); -} - -TEST(UnlinkTest, AtDir) { - int dirfd; - auto tmpdir = GetAbsoluteTestTmpdir(); - EXPECT_THAT(dirfd = open(tmpdir.c_str(), O_DIRECTORY, 0), SyscallSucceeds()); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(tmpdir)); - auto dir_relpath = - ASSERT_NO_ERRNO_AND_VALUE(GetRelativePath(tmpdir, dir.path())); - EXPECT_THAT(unlinkat(dirfd, dir_relpath.c_str(), AT_REMOVEDIR), - SyscallSucceeds()); - ASSERT_THAT(close(dirfd), SyscallSucceeds()); -} - -TEST(UnlinkTest, AtDirDegradedPermissions_NoRandomSave) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - int dirfd; - ASSERT_THAT(dirfd = open(dir.path().c_str(), O_DIRECTORY, 0), - SyscallSucceeds()); - - std::string sub_dir = JoinPath(dir.path(), "NewDir"); - EXPECT_THAT(mkdir(sub_dir.c_str(), 0755), SyscallSucceeds()); - EXPECT_THAT(fchmod(dirfd, 0444), SyscallSucceeds()); - EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), - SyscallFailsWithErrno(EACCES)); - ASSERT_THAT(close(dirfd), SyscallSucceeds()); -} - -// Files cannot be unlinked if the parent is not writable and executable. -TEST(UnlinkTest, ParentDegradedPermissions) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - - ASSERT_THAT(chmod(dir.path().c_str(), 0000), SyscallSucceeds()); - - struct stat st; - ASSERT_THAT(stat(file.path().c_str(), &st), SyscallFailsWithErrno(EACCES)); - ASSERT_THAT(unlinkat(AT_FDCWD, file.path().c_str(), 0), - SyscallFailsWithErrno(EACCES)); - - // Non-existent files also return EACCES. - const std::string nonexist = JoinPath(dir.path(), "doesnotexist"); - ASSERT_THAT(stat(nonexist.c_str(), &st), SyscallFailsWithErrno(EACCES)); - ASSERT_THAT(unlinkat(AT_FDCWD, nonexist.c_str(), 0), - SyscallFailsWithErrno(EACCES)); -} - -TEST(UnlinkTest, AtBad) { - int dirfd; - EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0), - SyscallSucceeds()); - - // Try removing a directory as a file. - std::string path = JoinPath(GetAbsoluteTestTmpdir(), "NewDir"); - EXPECT_THAT(mkdir(path.c_str(), 0755), SyscallSucceeds()); - EXPECT_THAT(unlinkat(dirfd, "NewDir", 0), SyscallFailsWithErrno(EISDIR)); - EXPECT_THAT(unlinkat(dirfd, "NewDir", AT_REMOVEDIR), SyscallSucceeds()); - - // Try removing a file as a directory. - int fd; - EXPECT_THAT(fd = openat(dirfd, "UnlinkAtFile", O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", AT_REMOVEDIR), - SyscallFailsWithErrno(ENOTDIR)); - EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile/", 0), - SyscallFailsWithErrno(ENOTDIR)); - ASSERT_THAT(close(fd), SyscallSucceeds()); - EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", 0), SyscallSucceeds()); - - // Cleanup. - ASSERT_THAT(close(dirfd), SyscallSucceeds()); -} - -TEST(UnlinkTest, AbsTmpFile) { - int fd; - std::string path = JoinPath(GetAbsoluteTestTmpdir(), "ExistingFile"); - EXPECT_THAT(fd = open(path.c_str(), O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - EXPECT_THAT(unlink(path.c_str()), SyscallSucceeds()); -} - -TEST(UnlinkTest, TooLongName) { - EXPECT_THAT(unlink(std::vector<char>(16384, '0').data()), - SyscallFailsWithErrno(ENAMETOOLONG)); -} - -TEST(UnlinkTest, BadNamePtr) { - EXPECT_THAT(unlink(reinterpret_cast<char*>(1)), - SyscallFailsWithErrno(EFAULT)); -} - -TEST(UnlinkTest, AtFile) { - int dirfd; - EXPECT_THAT(dirfd = open(GetAbsoluteTestTmpdir().c_str(), O_DIRECTORY, 0666), - SyscallSucceeds()); - int fd; - EXPECT_THAT(fd = openat(dirfd, "UnlinkAtFile", O_RDWR | O_CREAT, 0666), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); - EXPECT_THAT(unlinkat(dirfd, "UnlinkAtFile", 0), SyscallSucceeds()); -} - -TEST(UnlinkTest, OpenFile_NoRandomSave) { - // We can't save unlinked file unless they are on tmpfs. - const DisableSave ds; - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - int fd; - EXPECT_THAT(fd = open(file.path().c_str(), O_RDWR, 0666), SyscallSucceeds()); - EXPECT_THAT(unlink(file.path().c_str()), SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST(UnlinkTest, CannotRemoveDots) { - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const std::string self = JoinPath(file.path(), "."); - ASSERT_THAT(unlink(self.c_str()), SyscallFailsWithErrno(ENOTDIR)); - const std::string parent = JoinPath(file.path(), ".."); - ASSERT_THAT(unlink(parent.c_str()), SyscallFailsWithErrno(ENOTDIR)); -} - -TEST(UnlinkTest, CannotRemoveRoot) { - ASSERT_THAT(unlinkat(-1, "/", AT_REMOVEDIR), SyscallFailsWithErrno(EBUSY)); -} - -TEST(UnlinkTest, CannotRemoveRootWithAtDir) { - const FileDescriptor dirfd = ASSERT_NO_ERRNO_AND_VALUE( - Open(GetAbsoluteTestTmpdir(), O_DIRECTORY, 0666)); - ASSERT_THAT(unlinkat(dirfd.get(), "/", AT_REMOVEDIR), - SyscallFailsWithErrno(EBUSY)); -} - -TEST(RmdirTest, CannotRemoveDots) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string self = JoinPath(dir.path(), "."); - ASSERT_THAT(rmdir(self.c_str()), SyscallFailsWithErrno(EINVAL)); - const std::string parent = JoinPath(dir.path(), ".."); - ASSERT_THAT(rmdir(parent.c_str()), SyscallFailsWithErrno(ENOTEMPTY)); -} - -TEST(RmdirTest, CanRemoveWithTrailingSlashes) { - auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string slash = absl::StrCat(dir1.path(), "/"); - ASSERT_THAT(rmdir(slash.c_str()), SyscallSucceeds()); - auto dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const std::string slashslash = absl::StrCat(dir2.path(), "//"); - ASSERT_THAT(rmdir(slashslash.c_str()), SyscallSucceeds()); -} - -TEST(UnlinkTest, UnlinkAtEmptyPath) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); - EXPECT_THAT(unlinkat(fd.get(), "", 0), SyscallFailsWithErrno(ENOENT)); - - auto dirInDir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path())); - auto dirFD = ASSERT_NO_ERRNO_AND_VALUE( - Open(dirInDir.path(), O_RDONLY | O_DIRECTORY, 0666)); - EXPECT_THAT(unlinkat(dirFD.get(), "", AT_REMOVEDIR), - SyscallFailsWithErrno(ENOENT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/unshare.cc b/test/syscalls/linux/unshare.cc deleted file mode 100644 index e32619efe..000000000 --- a/test/syscalls/linux/unshare.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sched.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/synchronization/mutex.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -TEST(UnshareTest, AllowsZeroFlags) { - ASSERT_THAT(unshare(0), SyscallSucceeds()); -} - -TEST(UnshareTest, ThreadFlagFailsIfMultithreaded) { - absl::Mutex mu; - bool finished = false; - ScopedThread t([&] { - mu.Lock(); - mu.Await(absl::Condition(&finished)); - mu.Unlock(); - }); - ASSERT_THAT(unshare(CLONE_THREAD), SyscallFailsWithErrno(EINVAL)); - mu.Lock(); - finished = true; - mu.Unlock(); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc deleted file mode 100644 index e647d2896..000000000 --- a/test/syscalls/linux/utimes.cc +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <fcntl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/time.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> -#include <utime.h> - -#include <string> - -#include "absl/time/time.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// TimeBoxed runs fn, setting before and after to (coarse realtime) times -// guaranteed* to come before and after fn started and completed, respectively. -// -// fn may be called more than once if the clock is adjusted. -void TimeBoxed(absl::Time* before, absl::Time* after, - std::function<void()> const& fn) { - do { - // N.B. utimes and friends use CLOCK_REALTIME_COARSE for setting time (i.e., - // current_kernel_time()). See fs/attr.c:notify_change. - // - // notify_change truncates the time to a multiple of s_time_gran, but most - // filesystems set it to 1, so we don't do any truncation. - struct timespec ts; - EXPECT_THAT(clock_gettime(CLOCK_REALTIME_COARSE, &ts), SyscallSucceeds()); - // FIXME(b/132819225): gVisor filesystem timestamps inconsistently use the - // internal or host clock, which may diverge slightly. Allow some slack on - // times to account for the difference. - *before = absl::TimeFromTimespec(ts) - absl::Seconds(1); - - fn(); - - EXPECT_THAT(clock_gettime(CLOCK_REALTIME_COARSE, &ts), SyscallSucceeds()); - *after = absl::TimeFromTimespec(ts) + absl::Seconds(1); - - if (*after < *before) { - // Clock jumped backwards; retry. - // - // Technically this misses jumps small enough to keep after > before, - // which could lead to test failures, but that is very unlikely to happen. - continue; - } - } while (*after < *before); -} - -void TestUtimesOnPath(std::string const& path) { - struct stat statbuf; - - struct timeval times[2] = {{10, 0}, {20, 0}}; - EXPECT_THAT(utimes(path.c_str(), times), SyscallSucceeds()); - EXPECT_THAT(stat(path.c_str(), &statbuf), SyscallSucceeds()); - EXPECT_EQ(10, statbuf.st_atime); - EXPECT_EQ(20, statbuf.st_mtime); - - absl::Time before; - absl::Time after; - TimeBoxed(&before, &after, [&] { - EXPECT_THAT(utimes(path.c_str(), nullptr), SyscallSucceeds()); - }); - - EXPECT_THAT(stat(path.c_str(), &statbuf), SyscallSucceeds()); - - absl::Time atime = absl::TimeFromTimespec(statbuf.st_atim); - EXPECT_GE(atime, before); - EXPECT_LE(atime, after); - - absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim); - EXPECT_GE(mtime, before); - EXPECT_LE(mtime, after); -} - -TEST(UtimesTest, OnFile) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - TestUtimesOnPath(f.path()); -} - -TEST(UtimesTest, OnDir) { - auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TestUtimesOnPath(dir.path()); -} - -TEST(UtimesTest, MissingPath) { - auto path = NewTempAbsPath(); - struct timeval times[2] = {{10, 0}, {20, 0}}; - EXPECT_THAT(utimes(path.c_str(), times), SyscallFailsWithErrno(ENOENT)); -} - -void TestFutimesat(int dirFd, std::string const& path) { - struct stat statbuf; - - struct timeval times[2] = {{10, 0}, {20, 0}}; - EXPECT_THAT(futimesat(dirFd, path.c_str(), times), SyscallSucceeds()); - EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds()); - EXPECT_EQ(10, statbuf.st_atime); - EXPECT_EQ(20, statbuf.st_mtime); - - absl::Time before; - absl::Time after; - TimeBoxed(&before, &after, [&] { - EXPECT_THAT(futimesat(dirFd, path.c_str(), nullptr), SyscallSucceeds()); - }); - - EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds()); - - absl::Time atime = absl::TimeFromTimespec(statbuf.st_atim); - EXPECT_GE(atime, before); - EXPECT_LE(atime, after); - - absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim); - EXPECT_GE(mtime, before); - EXPECT_LE(mtime, after); -} - -TEST(FutimesatTest, OnAbsPath) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - TestFutimesat(0, f.path()); -} - -TEST(FutimesatTest, OnRelPath) { - auto d = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(d.path())); - auto basename = std::string(Basename(f.path())); - const FileDescriptor dirFd = - ASSERT_NO_ERRNO_AND_VALUE(Open(d.path(), O_RDONLY | O_DIRECTORY)); - TestFutimesat(dirFd.get(), basename); -} - -TEST(FutimesatTest, InvalidNsec) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - struct timeval times[4][2] = {{ - {0, 1}, // Valid - {1, static_cast<int64_t>(1e7)} // Invalid - }, - { - {1, static_cast<int64_t>(1e7)}, // Invalid - {0, 1} // Valid - }, - { - {0, 1}, // Valid - {1, -1} // Invalid - }, - { - {1, -1}, // Invalid - {0, 1} // Valid - }}; - - for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) { - std::cout << "test:" << i << "\n"; - EXPECT_THAT(futimesat(0, f.path().c_str(), times[i]), - SyscallFailsWithErrno(EINVAL)); - } -} - -void TestUtimensat(int dirFd, std::string const& path) { - struct stat statbuf; - const struct timespec times[2] = {{10, 0}, {20, 0}}; - EXPECT_THAT(utimensat(dirFd, path.c_str(), times, 0), SyscallSucceeds()); - EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf, 0), SyscallSucceeds()); - EXPECT_EQ(10, statbuf.st_atime); - EXPECT_EQ(20, statbuf.st_mtime); - - // Test setting with UTIME_NOW and UTIME_OMIT. - struct stat statbuf2; - const struct timespec times2[2] = { - {0, UTIME_NOW}, // Should set atime to now. - {0, UTIME_OMIT} // Should not change mtime. - }; - - absl::Time before; - absl::Time after; - TimeBoxed(&before, &after, [&] { - EXPECT_THAT(utimensat(dirFd, path.c_str(), times2, 0), SyscallSucceeds()); - }); - - EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf2, 0), SyscallSucceeds()); - - absl::Time atime2 = absl::TimeFromTimespec(statbuf2.st_atim); - EXPECT_GE(atime2, before); - EXPECT_LE(atime2, after); - - absl::Time mtime = absl::TimeFromTimespec(statbuf.st_mtim); - absl::Time mtime2 = absl::TimeFromTimespec(statbuf2.st_mtim); - // mtime should not be changed. - EXPECT_EQ(mtime, mtime2); - - // Test setting with times = NULL. Should set both atime and mtime to the - // current system time. - struct stat statbuf3; - TimeBoxed(&before, &after, [&] { - EXPECT_THAT(utimensat(dirFd, path.c_str(), nullptr, 0), SyscallSucceeds()); - }); - - EXPECT_THAT(fstatat(dirFd, path.c_str(), &statbuf3, 0), SyscallSucceeds()); - - absl::Time atime3 = absl::TimeFromTimespec(statbuf3.st_atim); - EXPECT_GE(atime3, before); - EXPECT_LE(atime3, after); - - absl::Time mtime3 = absl::TimeFromTimespec(statbuf3.st_mtim); - EXPECT_GE(mtime3, before); - EXPECT_LE(mtime3, after); - - EXPECT_EQ(atime3, mtime3); -} - -TEST(UtimensatTest, OnAbsPath) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - TestUtimensat(0, f.path()); -} - -TEST(UtimensatTest, OnRelPath) { - auto d = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(d.path())); - auto basename = std::string(Basename(f.path())); - const FileDescriptor dirFd = - ASSERT_NO_ERRNO_AND_VALUE(Open(d.path(), O_RDONLY | O_DIRECTORY)); - TestUtimensat(dirFd.get(), basename); -} - -TEST(UtimensatTest, OmitNoop) { - // Setting both timespecs to UTIME_OMIT on a nonexistant path should succeed. - auto path = NewTempAbsPath(); - const struct timespec times[2] = {{0, UTIME_OMIT}, {0, UTIME_OMIT}}; - EXPECT_THAT(utimensat(0, path.c_str(), times, 0), SyscallSucceeds()); -} - -// Verify that we can actually set atime and mtime to 0. -TEST(UtimeTest, ZeroAtimeandMtime) { - const auto tmp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const auto tmp_file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmp_dir.path())); - - // Stat the file before and after updating atime and mtime. - struct stat stat_before = {}; - EXPECT_THAT(stat(tmp_file.path().c_str(), &stat_before), SyscallSucceeds()); - - ASSERT_NE(stat_before.st_atime, 0); - ASSERT_NE(stat_before.st_mtime, 0); - - const struct utimbuf times = {}; // Zero for both atime and mtime. - EXPECT_THAT(utime(tmp_file.path().c_str(), ×), SyscallSucceeds()); - - struct stat stat_after = {}; - EXPECT_THAT(stat(tmp_file.path().c_str(), &stat_after), SyscallSucceeds()); - - // We should see the atime and mtime changed when we set them to 0. - ASSERT_EQ(stat_after.st_atime, 0); - ASSERT_EQ(stat_after.st_mtime, 0); -} - -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 - }}; - - for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) { - std::cout << "test:" << i << "\n"; - EXPECT_THAT(utimensat(0, f.path().c_str(), times[i], 0), - SyscallFailsWithErrno(EINVAL)); - } -} - -TEST(Utimensat, NullPath) { - // From man utimensat(2): - // "the Linux utimensat() system call implements a nonstandard feature: if - // pathname is NULL, then the call modifies the timestamps of the file - // referred to by the file descriptor dirfd (which may refer to any type of - // file). - // Note, however, that the glibc wrapper for utimensat() disallows - // passing NULL as the value for file: the wrapper function returns the error - // EINVAL in this case." - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); - struct stat statbuf; - const struct timespec times[2] = {{10, 0}, {20, 0}}; - // Call syscall directly. - EXPECT_THAT(syscall(SYS_utimensat, fd.get(), NULL, times, 0), - SyscallSucceeds()); - EXPECT_THAT(fstatat(0, f.path().c_str(), &statbuf, 0), SyscallSucceeds()); - EXPECT_EQ(10, statbuf.st_atime); - EXPECT_EQ(20, statbuf.st_mtime); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/vdso.cc b/test/syscalls/linux/vdso.cc deleted file mode 100644 index 19c80add8..000000000 --- a/test/syscalls/linux/vdso.cc +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <string.h> -#include <sys/mman.h> - -#include <algorithm> - -#include "gtest/gtest.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/proc_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Ensure that the vvar page cannot be made writable. -TEST(VvarTest, WriteVvar) { - auto contents = ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps")); - auto maps = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMaps(contents)); - auto it = std::find_if(maps.begin(), maps.end(), [](const ProcMapsEntry& e) { - return e.filename == "[vvar]"; - }); - - SKIP_IF(it == maps.end()); - EXPECT_THAT(mprotect(reinterpret_cast<void*>(it->start), kPageSize, - PROT_READ | PROT_WRITE), - SyscallFailsWithErrno(EACCES)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/vdso_clock_gettime.cc b/test/syscalls/linux/vdso_clock_gettime.cc deleted file mode 100644 index 2a8699a7b..000000000 --- a/test/syscalls/linux/vdso_clock_gettime.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <stdint.h> -#include <sys/time.h> -#include <syscall.h> -#include <time.h> -#include <unistd.h> - -#include <map> -#include <string> -#include <utility> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/numbers.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) { - switch (info.param) { - case CLOCK_MONOTONIC: - return "CLOCK_MONOTONIC"; - case CLOCK_BOOTTIME: - return "CLOCK_BOOTTIME"; - default: - return absl::StrCat(info.param); - } -} - -class MonotonicVDSOClockTest : public ::testing::TestWithParam<clockid_t> {}; - -TEST_P(MonotonicVDSOClockTest, IsCorrect) { - // The VDSO implementation of clock_gettime() uses the TSC. On KVM, sentry and - // application TSCs can be very desynchronized; see - // sentry/platform/kvm/kvm.vCPU.setSystemTime(). - SKIP_IF(GvisorPlatform() == Platform::kKVM); - - // Check that when we alternate readings from the clock_gettime syscall and - // the VDSO's implementation, we observe the combined sequence as being - // monotonic. - struct timespec tvdso, tsys; - absl::Time vdso_time, sys_time; - ASSERT_THAT(syscall(__NR_clock_gettime, GetParam(), &tsys), - SyscallSucceeds()); - sys_time = absl::TimeFromTimespec(tsys); - auto end = absl::Now() + absl::Seconds(10); - while (absl::Now() < end) { - ASSERT_THAT(clock_gettime(GetParam(), &tvdso), SyscallSucceeds()); - vdso_time = absl::TimeFromTimespec(tvdso); - EXPECT_LE(sys_time, vdso_time); - ASSERT_THAT(syscall(__NR_clock_gettime, GetParam(), &tsys), - SyscallSucceeds()); - sys_time = absl::TimeFromTimespec(tsys); - EXPECT_LE(vdso_time, sys_time); - } -} - -INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicVDSOClockTest, - ::testing::Values(CLOCK_MONOTONIC, CLOCK_BOOTTIME), - PrintClockId); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/vfork.cc b/test/syscalls/linux/vfork.cc deleted file mode 100644 index 19d05998e..000000000 --- a/test/syscalls/linux/vfork.cc +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <string> -#include <utility> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/flags/flag.h" -#include "absl/time/time.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/test_util.h" -#include "test/util/time_util.h" - -ABSL_FLAG(bool, vfork_test_child, false, - "If true, run the VforkTest child workload."); - -namespace gvisor { -namespace testing { - -namespace { - -// We don't test with raw CLONE_VFORK to avoid interacting with glibc's use of -// TLS. -// -// Even with vfork(2), we must be careful to do little more in the child than -// call execve(2). We use the simplest sleep function possible, though this is -// still precarious, as we're officially only allowed to call execve(2) and -// _exit(2). -constexpr absl::Duration kChildDelay = absl::Seconds(10); - -// Exit code for successful child subprocesses. We don't want to use 0 since -// it's too common, and an execve(2) failure causes the child to exit with the -// errno, so kChildExitCode is chosen to be an unlikely errno: -constexpr int kChildExitCode = 118; // ENOTNAM: Not a XENIX named type file - -int64_t MonotonicNow() { - struct timespec now; - TEST_PCHECK(clock_gettime(CLOCK_MONOTONIC, &now) == 0); - return now.tv_sec * 1000000000ll + now.tv_nsec; -} - -TEST(VforkTest, ParentStopsUntilChildExits) { - const auto test = [] { - // N.B. Run the test in a single-threaded subprocess because - // vfork is not safe in a multi-threaded process. - - const int64_t start = MonotonicNow(); - - pid_t pid = vfork(); - if (pid == 0) { - SleepSafe(kChildDelay); - _exit(kChildExitCode); - } - TEST_PCHECK_MSG(pid > 0, "vfork failed"); - MaybeSave(); - - const int64_t end = MonotonicNow(); - - absl::Duration dur = absl::Nanoseconds(end - start); - - TEST_CHECK(dur >= kChildDelay); - - int status = 0; - TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0)); - TEST_CHECK(WIFEXITED(status)); - TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); - }; - - EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0)); -} - -TEST(VforkTest, ParentStopsUntilChildExecves_NoRandomSave) { - ExecveArray const owned_child_argv = {"/proc/self/exe", "--vfork_test_child"}; - char* const* const child_argv = owned_child_argv.get(); - - const auto test = [&] { - const int64_t start = MonotonicNow(); - - pid_t pid = vfork(); - if (pid == 0) { - SleepSafe(kChildDelay); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - _exit(errno); - } - // Don't attempt save/restore until after recording end_time, - // since the test expects an upper bound on the time spent - // stopped. - int saved_errno = errno; - const int64_t end = MonotonicNow(); - errno = saved_errno; - TEST_PCHECK_MSG(pid > 0, "vfork failed"); - MaybeSave(); - - absl::Duration dur = absl::Nanoseconds(end - start); - - // The parent should resume execution after execve, but before - // the post-execve test child exits. - TEST_CHECK(dur >= kChildDelay); - TEST_CHECK(dur <= 2 * kChildDelay); - - int status = 0; - TEST_PCHECK(RetryEINTR(waitpid)(pid, &status, 0)); - TEST_CHECK(WIFEXITED(status)); - TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); - }; - - EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0)); -} - -// A vfork child does not unstop the parent a second time when it exits after -// exec. -TEST(VforkTest, ExecedChildExitDoesntUnstopParent_NoRandomSave) { - ExecveArray const owned_child_argv = {"/proc/self/exe", "--vfork_test_child"}; - char* const* const child_argv = owned_child_argv.get(); - - const auto test = [&] { - pid_t pid1 = vfork(); - if (pid1 == 0) { - execve(child_argv[0], child_argv, /* envp = */ nullptr); - _exit(errno); - } - TEST_PCHECK_MSG(pid1 > 0, "vfork failed"); - MaybeSave(); - - // pid1 exec'd and is now sleeping. - SleepSafe(kChildDelay / 2); - - const int64_t start = MonotonicNow(); - - pid_t pid2 = vfork(); - if (pid2 == 0) { - SleepSafe(kChildDelay); - _exit(kChildExitCode); - } - TEST_PCHECK_MSG(pid2 > 0, "vfork failed"); - MaybeSave(); - - const int64_t end = MonotonicNow(); - - absl::Duration dur = absl::Nanoseconds(end - start); - - // The parent should resume execution only after pid2 exits, not - // when pid1 exits. - TEST_CHECK(dur >= kChildDelay); - - int status = 0; - TEST_PCHECK(RetryEINTR(waitpid)(pid1, &status, 0)); - TEST_CHECK(WIFEXITED(status)); - TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); - - TEST_PCHECK(RetryEINTR(waitpid)(pid2, &status, 0)); - TEST_CHECK(WIFEXITED(status)); - TEST_CHECK(WEXITSTATUS(status) == kChildExitCode); - }; - - EXPECT_THAT(InForkedProcess(test), IsPosixErrorOkAndHolds(0)); -} - -int RunChild() { - SleepSafe(kChildDelay); - return kChildExitCode; -} - -} // namespace - -} // namespace testing -} // namespace gvisor - -int main(int argc, char** argv) { - gvisor::testing::TestInit(&argc, &argv); - - if (absl::GetFlag(FLAGS_vfork_test_child)) { - return gvisor::testing::RunChild(); - } - - return gvisor::testing::RunAllTests(); -} diff --git a/test/syscalls/linux/vsyscall.cc b/test/syscalls/linux/vsyscall.cc deleted file mode 100644 index ae4377108..000000000 --- a/test/syscalls/linux/vsyscall.cc +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <time.h> - -#include "gtest/gtest.h" -#include "test/util/proc_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -#if defined(__x86_64__) || defined(__i386__) -time_t vsyscall_time(time_t* t) { - constexpr uint64_t kVsyscallTimeEntry = 0xffffffffff600400; - return reinterpret_cast<time_t (*)(time_t*)>(kVsyscallTimeEntry)(t); -} - -TEST(VsyscallTest, VsyscallAlwaysAvailableOnGvisor) { - SKIP_IF(!IsRunningOnGvisor()); - // Vsyscall is always advertised by gvisor. - EXPECT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(IsVsyscallEnabled())); - // Vsyscall should always works on gvisor. - time_t t; - EXPECT_THAT(vsyscall_time(&t), SyscallSucceeds()); -} -#endif - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/wait.cc b/test/syscalls/linux/wait.cc deleted file mode 100644 index 944149d5e..000000000 --- a/test/syscalls/linux/wait.cc +++ /dev/null @@ -1,913 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <signal.h> -#include <sys/mman.h> -#include <sys/ptrace.h> -#include <sys/resource.h> -#include <sys/time.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -#include <functional> -#include <tuple> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "absl/synchronization/mutex.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/multiprocess_util.h" -#include "test/util/posix_error.h" -#include "test/util/signal_util.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" -#include "test/util/time_util.h" - -using ::testing::UnorderedElementsAre; - -// These unit tests focus on the wait4(2) system call, but include a basic -// checks for the i386 waitpid(2) syscall, which is a subset of wait4(2). -// -// NOTE(b/22640830,b/27680907,b/29049891): Some functionality is not tested as -// it is not currently supported by gVisor: -// * Process groups. -// * Core dump status (WCOREDUMP). -// -// Tests for waiting on stopped/continued children are in sigstop.cc. - -namespace gvisor { -namespace testing { - -namespace { - -// The CloneChild function seems to need more than one page of stack space. -static const size_t kStackSize = 2 * kPageSize; - -// The child thread created in CloneAndExit runs this function. -// This child does not have the TLS setup, so it must not use glibc functions. -int CloneChild(void* priv) { - int64_t sleep = reinterpret_cast<int64_t>(priv); - SleepSafe(absl::Seconds(sleep)); - - // glibc's _exit(2) function wrapper will helpfully call exit_group(2), - // exiting the entire process. - syscall(__NR_exit, 0); - return 1; -} - -// ForkAndExit forks a child process which exits with exit_code, after -// sleeping for the specified duration (seconds). -pid_t ForkAndExit(int exit_code, int64_t sleep) { - pid_t child = fork(); - if (child == 0) { - SleepSafe(absl::Seconds(sleep)); - _exit(exit_code); - } - return child; -} - -int64_t clock_gettime_nsecs(clockid_t id) { - struct timespec ts; - TEST_PCHECK(clock_gettime(id, &ts) == 0); - return (ts.tv_sec * 1000000000 + ts.tv_nsec); -} - -void spin(int64_t sec) { - int64_t ns = sec * 1000000000; - int64_t start = clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID); - int64_t end = start + ns; - - do { - constexpr int kLoopCount = 1000000; // large and arbitrary - // volatile to prevent the compiler from skipping this loop. - for (volatile int i = 0; i < kLoopCount; i++) { - } - } while (clock_gettime_nsecs(CLOCK_THREAD_CPUTIME_ID) < end); -} - -// ForkSpinAndExit forks a child process which exits with exit_code, after -// spinning for the specified duration (seconds). -pid_t ForkSpinAndExit(int exit_code, int64_t spintime) { - pid_t child = fork(); - if (child == 0) { - spin(spintime); - _exit(exit_code); - } - return child; -} - -absl::Duration RusageCpuTime(const struct rusage& ru) { - return absl::DurationFromTimeval(ru.ru_utime) + - absl::DurationFromTimeval(ru.ru_stime); -} - -// Returns the address of the top of the stack. -// Free with FreeStack. -uintptr_t AllocStack() { - void* addr = mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - if (addr == MAP_FAILED) { - return reinterpret_cast<uintptr_t>(MAP_FAILED); - } - - return reinterpret_cast<uintptr_t>(addr) + kStackSize; -} - -// Frees a stack page allocated with AllocStack. -int FreeStack(uintptr_t addr) { - addr -= kStackSize; - return munmap(reinterpret_cast<void*>(addr), kPageSize); -} - -// CloneAndExit clones a child thread, which exits with 0 after sleeping for -// the specified duration (must be in seconds). extra_flags are ORed against -// the standard clone(2) flags. -int CloneAndExit(int64_t sleep, uintptr_t stack, int extra_flags) { - return clone(CloneChild, reinterpret_cast<void*>(stack), - CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_VM | extra_flags, - reinterpret_cast<void*>(sleep)); -} - -// Simple wrappers around wait4(2) and waitid(2) that ignore interrupts. -constexpr auto Wait4 = RetryEINTR(wait4); -constexpr auto Waitid = RetryEINTR(waitid); - -// Fixture for tests parameterized by a function that waits for any child to -// exit with the given options, checks that it exited with the given code, and -// then returns its PID. -// -// N.B. These tests run in a multi-threaded environment. We assume that -// background threads do not create child processes and are not themselves -// created with clone(... | SIGCHLD). Either may cause these tests to -// erroneously wait on child processes/threads. -class WaitAnyChildTest : public ::testing::TestWithParam< - std::function<PosixErrorOr<pid_t>(int, int)>> { - protected: - PosixErrorOr<pid_t> WaitAny(int code) { return WaitAnyWithOptions(code, 0); } - - PosixErrorOr<pid_t> WaitAnyWithOptions(int code, int options) { - return GetParam()(code, options); - } -}; - -// Wait for any child to exit. -TEST_P(WaitAnyChildTest, Fork) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - - EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); -} - -// Call wait4 for any process after the child has already exited. -TEST_P(WaitAnyChildTest, AfterExit) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - - absl::SleepFor(absl::Seconds(5)); - - EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); -} - -// Wait for multiple children to exit, waiting for either at a time. -TEST_P(WaitAnyChildTest, MultipleFork) { - pid_t child1, child2; - ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds()); - ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds()); - - std::vector<pid_t> pids; - pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); - pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); - EXPECT_THAT(pids, UnorderedElementsAre(child1, child2)); -} - -// Wait for any child to exit. -// A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like -// a forked process. -TEST_P(WaitAnyChildTest, CloneSIGCHLD) { - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int child; - ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); - - EXPECT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); -} - -// Wait for a child thread and process. -TEST_P(WaitAnyChildTest, ForkAndClone) { - pid_t process; - ASSERT_THAT(process = ForkAndExit(0, 0), SyscallSucceeds()); - - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int thread; - // Send SIGCHLD for normal wait semantics. - ASSERT_THAT(thread = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); - - std::vector<pid_t> pids; - pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); - pids.push_back(ASSERT_NO_ERRNO_AND_VALUE(WaitAny(0))); - EXPECT_THAT(pids, UnorderedElementsAre(process, thread)); -} - -// Return immediately if no child has exited. -TEST_P(WaitAnyChildTest, WaitWNOHANG) { - EXPECT_THAT(WaitAnyWithOptions(0, WNOHANG), - PosixErrorIs(ECHILD, ::testing::_)); -} - -// Bad options passed -TEST_P(WaitAnyChildTest, BadOption) { - EXPECT_THAT(WaitAnyWithOptions(0, 123456), - PosixErrorIs(EINVAL, ::testing::_)); -} - -TEST_P(WaitAnyChildTest, WaitedChildRusage) { - struct rusage before; - ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &before), SyscallSucceeds()); - - pid_t child; - constexpr absl::Duration kSpin = absl::Seconds(3); - ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)), - SyscallSucceeds()); - ASSERT_THAT(WaitAny(0), IsPosixErrorOkAndHolds(child)); - - struct rusage after; - ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &after), SyscallSucceeds()); - - EXPECT_GE(RusageCpuTime(after) - RusageCpuTime(before), kSpin); -} - -TEST_P(WaitAnyChildTest, IgnoredChildRusage) { - // "POSIX.1-2001 specifies that if the disposition of SIGCHLD is - // set to SIG_IGN or the SA_NOCLDWAIT flag is set for SIGCHLD (see - // sigaction(2)), then children that terminate do not become zombies and a - // call to wait() or waitpid() will block until all children have terminated, - // and then fail with errno set to ECHILD." - waitpid(2) - // - // "RUSAGE_CHILDREN: Return resource usage statistics for all children of the - // calling process that have terminated *and been waited for*." - - // getrusage(2), emphasis added - - struct sigaction sa; - sa.sa_handler = SIG_IGN; - const auto cleanup_sigact = - ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGCHLD, sa)); - - struct rusage before; - ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &before), SyscallSucceeds()); - - const absl::Duration start = - absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC)); - - constexpr absl::Duration kSpin = absl::Seconds(3); - - // ForkAndSpin uses CLOCK_THREAD_CPUTIME_ID, which is lower resolution than, - // and may diverge from, CLOCK_MONOTONIC, so we allow a small grace period but - // still check that we blocked for a while. - constexpr absl::Duration kSpinGrace = absl::Milliseconds(100); - - pid_t child; - ASSERT_THAT(child = ForkSpinAndExit(0, absl::ToInt64Seconds(kSpin)), - SyscallSucceeds()); - ASSERT_THAT(WaitAny(0), PosixErrorIs(ECHILD, ::testing::_)); - const absl::Duration end = - absl::Nanoseconds(clock_gettime_nsecs(CLOCK_MONOTONIC)); - EXPECT_GE(end - start, kSpin - kSpinGrace); - - struct rusage after; - ASSERT_THAT(getrusage(RUSAGE_CHILDREN, &after), SyscallSucceeds()); - EXPECT_EQ(before.ru_utime.tv_sec, after.ru_utime.tv_sec); - EXPECT_EQ(before.ru_utime.tv_usec, after.ru_utime.tv_usec); - EXPECT_EQ(before.ru_stime.tv_sec, after.ru_stime.tv_sec); - EXPECT_EQ(before.ru_stime.tv_usec, after.ru_stime.tv_usec); -} - -INSTANTIATE_TEST_SUITE_P( - Waiters, WaitAnyChildTest, - ::testing::Values( - [](int code, int options) -> PosixErrorOr<pid_t> { - int status; - auto const pid = Wait4(-1, &status, options, nullptr); - MaybeSave(); - if (pid < 0) { - return PosixError(errno, "wait4"); - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != code) { - return PosixError( - EINVAL, absl::StrCat("unexpected wait status: got ", status, - ", wanted ", code)); - } - return static_cast<pid_t>(pid); - }, - [](int code, int options) -> PosixErrorOr<pid_t> { - siginfo_t si; - auto const rv = Waitid(P_ALL, 0, &si, WEXITED | options); - MaybeSave(); - if (rv < 0) { - return PosixError(errno, "waitid"); - } - if (si.si_signo != SIGCHLD) { - return PosixError( - EINVAL, absl::StrCat("unexpected signo: got ", si.si_signo, - ", wanted ", SIGCHLD)); - } - if (si.si_status != code) { - return PosixError( - EINVAL, absl::StrCat("unexpected status: got ", si.si_status, - ", wanted ", code)); - } - if (si.si_code != CLD_EXITED) { - return PosixError(EINVAL, - absl::StrCat("unexpected code: got ", si.si_code, - ", wanted ", CLD_EXITED)); - } - auto const uid = getuid(); - if (si.si_uid != uid) { - return PosixError(EINVAL, - absl::StrCat("unexpected uid: got ", si.si_uid, - ", wanted ", uid)); - } - return static_cast<pid_t>(si.si_pid); - })); - -// Fixture for tests parameterized by a (sysno, function) tuple. The function -// takes the PID of a specific child to wait for, waits for it to exit, and -// checks that it exits with the given code. -class WaitSpecificChildTest - : public ::testing::TestWithParam< - std::tuple<int, std::function<PosixError(pid_t, int, int)>>> { - protected: - int Sysno() { return std::get<0>(GetParam()); } - - PosixError WaitForWithOptions(pid_t pid, int options, int code) { - return std::get<1>(GetParam())(pid, options, code); - } - - PosixError WaitFor(pid_t pid, int code) { - return std::get<1>(GetParam())(pid, 0, code); - } -}; - -// Wait for specific child to exit. -TEST_P(WaitSpecificChildTest, Fork) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -// Non-zero exit codes are correctly propagated. -TEST_P(WaitSpecificChildTest, NormalExit) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child, 42)); -} - -// Wait for multiple children to exit. -TEST_P(WaitSpecificChildTest, MultipleFork) { - pid_t child1, child2; - ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds()); - ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child1, 0)); - EXPECT_NO_ERRNO(WaitFor(child2, 0)); -} - -// Wait for multiple children to exit, out of the order they were created. -TEST_P(WaitSpecificChildTest, MultipleForkOutOfOrder) { - pid_t child1, child2; - ASSERT_THAT(child1 = ForkAndExit(0, 0), SyscallSucceeds()); - ASSERT_THAT(child2 = ForkAndExit(0, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child2, 0)); - EXPECT_NO_ERRNO(WaitFor(child1, 0)); -} - -// Wait for specific child to exit, entering wait4 before the exit occurs. -TEST_P(WaitSpecificChildTest, ForkSleep) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 5), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -// Wait should block until the child exits. -TEST_P(WaitSpecificChildTest, ForkBlock) { - pid_t child; - - auto start = absl::Now(); - ASSERT_THAT(child = ForkAndExit(0, 5), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); - - EXPECT_GE(absl::Now() - start, absl::Seconds(5)); -} - -// Waiting after the child has already exited returns immediately. -TEST_P(WaitSpecificChildTest, AfterExit) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - - absl::SleepFor(absl::Seconds(5)); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -// Wait for child of sibling thread. -TEST_P(WaitSpecificChildTest, SiblingChildren) { - absl::Mutex mu; - pid_t child; - bool ready = false; - bool stop = false; - - ScopedThread t([&] { - absl::MutexLock ml(&mu); - EXPECT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - ready = true; - mu.Await(absl::Condition(&stop)); - }); - - // N.B. This must be declared after ScopedThread, so it is destructed first, - // thus waking the thread. - absl::MutexLock ml(&mu); - mu.Await(absl::Condition(&ready)); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); - - // Keep the sibling alive until after we've waited so the child isn't - // reparented. - stop = true; -} - -// Waiting for child of sibling thread not allowed with WNOTHREAD. -TEST_P(WaitSpecificChildTest, SiblingChildrenWNOTHREAD) { - // Linux added WNOTHREAD support to waitid(2) in - // 91c4e8ea8f05916df0c8a6f383508ac7c9e10dba ("wait: allow sys_waitid() to - // accept __WNOTHREAD/__WCLONE/__WALL"). i.e., Linux 4.7. - // - // Skip the test if it isn't supported yet. - if (Sysno() == SYS_waitid) { - int ret = waitid(P_ALL, 0, nullptr, WEXITED | WNOHANG | __WNOTHREAD); - SKIP_IF(ret < 0 && errno == EINVAL); - } - - absl::Mutex mu; - pid_t child; - bool ready = false; - bool stop = false; - - ScopedThread t([&] { - absl::MutexLock ml(&mu); - EXPECT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - ready = true; - mu.Await(absl::Condition(&stop)); - - // This thread can wait on child. - EXPECT_NO_ERRNO(WaitForWithOptions(child, __WNOTHREAD, 0)); - }); - - // N.B. This must be declared after ScopedThread, so it is destructed first, - // thus waking the thread. - absl::MutexLock ml(&mu); - mu.Await(absl::Condition(&ready)); - - // This thread can't wait on child. - EXPECT_THAT(WaitForWithOptions(child, __WNOTHREAD, 0), - PosixErrorIs(ECHILD, ::testing::_)); - - // Keep the sibling alive until after we've waited so the child isn't - // reparented. - stop = true; -} - -// Wait for specific child to exit. -// A non-CLONE_THREAD child which sends SIGCHLD upon exit behaves much like -// a forked process. -TEST_P(WaitSpecificChildTest, CloneSIGCHLD) { - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int child; - ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -// Wait for specific child to exit. -// A non-CLONE_THREAD child which does not send SIGCHLD upon exit can be waited -// on, but returns ECHILD. -TEST_P(WaitSpecificChildTest, CloneNoSIGCHLD) { - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int child; - ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds()); - - EXPECT_THAT(WaitFor(child, 0), PosixErrorIs(ECHILD, ::testing::_)); -} - -// Waiting after the child has already exited returns immediately. -TEST_P(WaitSpecificChildTest, CloneAfterExit) { - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int child; - // Send SIGCHLD for normal wait semantics. - ASSERT_THAT(child = CloneAndExit(0, stack, SIGCHLD), SyscallSucceeds()); - - absl::SleepFor(absl::Seconds(5)); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -// A CLONE_THREAD child cannot be waited on. -TEST_P(WaitSpecificChildTest, CloneThread) { - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int child; - ASSERT_THAT(child = CloneAndExit(15, stack, CLONE_THREAD), SyscallSucceeds()); - auto start = absl::Now(); - - EXPECT_THAT(WaitFor(child, 0), PosixErrorIs(ECHILD, ::testing::_)); - - // Ensure wait4 didn't block. - EXPECT_LE(absl::Now() - start, absl::Seconds(10)); - - // Since we can't wait on the child, we sleep to try to avoid freeing its - // stack before it exits. - absl::SleepFor(absl::Seconds(5)); -} - -// A child that does not send a SIGCHLD on exit may be waited on with -// the __WCLONE flag. -TEST_P(WaitSpecificChildTest, CloneWCLONE) { - // Linux added WCLONE support to waitid(2) in - // 91c4e8ea8f05916df0c8a6f383508ac7c9e10dba ("wait: allow sys_waitid() to - // accept __WNOTHREAD/__WCLONE/__WALL"). i.e., Linux 4.7. - // - // Skip the test if it isn't supported yet. - if (Sysno() == SYS_waitid) { - int ret = waitid(P_ALL, 0, nullptr, WEXITED | WNOHANG | __WCLONE); - SKIP_IF(ret < 0 && errno == EINVAL); - } - - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - int child; - ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitForWithOptions(child, __WCLONE, 0)); -} - -// A forked child cannot be waited on with WCLONE. -TEST_P(WaitSpecificChildTest, ForkWCLONE) { - // Linux added WCLONE support to waitid(2) in - // 91c4e8ea8f05916df0c8a6f383508ac7c9e10dba ("wait: allow sys_waitid() to - // accept __WNOTHREAD/__WCLONE/__WALL"). i.e., Linux 4.7. - // - // Skip the test if it isn't supported yet. - if (Sysno() == SYS_waitid) { - int ret = waitid(P_ALL, 0, nullptr, WEXITED | WNOHANG | __WCLONE); - SKIP_IF(ret < 0 && errno == EINVAL); - } - - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - - EXPECT_THAT(WaitForWithOptions(child, WNOHANG | __WCLONE, 0), - PosixErrorIs(ECHILD, ::testing::_)); - - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -// Any type of child can be waited on with WALL. -TEST_P(WaitSpecificChildTest, WALL) { - // Linux added WALL support to waitid(2) in - // 91c4e8ea8f05916df0c8a6f383508ac7c9e10dba ("wait: allow sys_waitid() to - // accept __WNOTHREAD/__WCLONE/__WALL"). i.e., Linux 4.7. - // - // Skip the test if it isn't supported yet. - if (Sysno() == SYS_waitid) { - int ret = waitid(P_ALL, 0, nullptr, WEXITED | WNOHANG | __WALL); - SKIP_IF(ret < 0 && errno == EINVAL); - } - - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitForWithOptions(child, __WALL, 0)); - - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - ASSERT_THAT(child = CloneAndExit(0, stack, 0), SyscallSucceeds()); - - EXPECT_NO_ERRNO(WaitForWithOptions(child, __WALL, 0)); -} - -// Return ECHILD for bad child. -TEST_P(WaitSpecificChildTest, BadChild) { - EXPECT_THAT(WaitFor(42, 0), PosixErrorIs(ECHILD, ::testing::_)); -} - -// Wait for a child process that only exits after calling execve(2) from a -// non-leader thread. -TEST_P(WaitSpecificChildTest, AfterChildExecve) { - ExecveArray const owned_child_argv = {"/bin/true"}; - char* const* const child_argv = owned_child_argv.get(); - - uintptr_t stack; - ASSERT_THAT(stack = AllocStack(), SyscallSucceeds()); - auto free = - Cleanup([stack] { ASSERT_THAT(FreeStack(stack), SyscallSucceeds()); }); - - pid_t const child = fork(); - if (child == 0) { - // Give the parent some time to start waiting. - SleepSafe(absl::Seconds(5)); - // Pass CLONE_VFORK to block the original thread in the child process until - // the clone thread calls execve, annihilating them both. (This means that - // if clone returns at all, something went wrong.) - // - // N.B. clone(2) is not officially async-signal-safe, but at minimum glibc's - // x86_64 implementation is safe. See glibc - // sysdeps/unix/sysv/linux/x86_64/clone.S. - clone( - +[](void* arg) { - auto child_argv = static_cast<char* const*>(arg); - execve(child_argv[0], child_argv, /* envp = */ nullptr); - return errno; - }, - reinterpret_cast<void*>(stack), - CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD | CLONE_VM | - CLONE_VFORK, - const_cast<char**>(child_argv)); - _exit(errno); - } - ASSERT_THAT(child, SyscallSucceeds()); - EXPECT_NO_ERRNO(WaitFor(child, 0)); -} - -PosixError CheckWait4(pid_t pid, int options, int code) { - int status; - auto const rv = Wait4(pid, &status, options, nullptr); - MaybeSave(); - if (rv < 0) { - return PosixError(errno, "wait4"); - } else if (rv != pid) { - return PosixError( - EINVAL, absl::StrCat("unexpected pid: got ", rv, ", wanted ", pid)); - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != code) { - return PosixError(EINVAL, absl::StrCat("unexpected wait status: got ", - status, ", wanted ", code)); - } - return NoError(); -}; - -PosixError CheckWaitid(pid_t pid, int options, int code) { - siginfo_t si; - auto const rv = Waitid(P_PID, pid, &si, options | WEXITED); - MaybeSave(); - if (rv < 0) { - return PosixError(errno, "waitid"); - } - if (si.si_pid != pid) { - return PosixError(EINVAL, absl::StrCat("unexpected pid: got ", si.si_pid, - ", wanted ", pid)); - } - if (si.si_signo != SIGCHLD) { - return PosixError(EINVAL, absl::StrCat("unexpected signo: got ", - si.si_signo, ", wanted ", SIGCHLD)); - } - if (si.si_status != code) { - return PosixError(EINVAL, absl::StrCat("unexpected status: got ", - si.si_status, ", wanted ", code)); - } - if (si.si_code != CLD_EXITED) { - return PosixError(EINVAL, absl::StrCat("unexpected code: got ", si.si_code, - ", wanted ", CLD_EXITED)); - } - return NoError(); -} - -INSTANTIATE_TEST_SUITE_P( - Waiters, WaitSpecificChildTest, - ::testing::Values(std::make_tuple(SYS_wait4, CheckWait4), - std::make_tuple(SYS_waitid, CheckWaitid))); - -// WIFEXITED, WIFSIGNALED, WTERMSIG indicate signal exit. -TEST(WaitTest, SignalExit) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 10), SyscallSucceeds()); - - EXPECT_THAT(kill(child, SIGKILL), SyscallSucceeds()); - - int status; - EXPECT_THAT(Wait4(child, &status, 0, nullptr), - SyscallSucceedsWithValue(child)); - - EXPECT_FALSE(WIFEXITED(status)); - EXPECT_TRUE(WIFSIGNALED(status)); - EXPECT_EQ(SIGKILL, WTERMSIG(status)); -} - -// waitid requires at least one option. -TEST(WaitTest, WaitidOptions) { - EXPECT_THAT(Waitid(P_ALL, 0, nullptr, 0), SyscallFailsWithErrno(EINVAL)); -} - -// waitid does not wait for a child to exit if not passed WEXITED. -TEST(WaitTest, WaitidNoWEXITED) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(0, 0), SyscallSucceeds()); - EXPECT_THAT(Waitid(P_ALL, 0, nullptr, WSTOPPED), - SyscallFailsWithErrno(ECHILD)); - EXPECT_THAT(Waitid(P_ALL, 0, nullptr, WEXITED), SyscallSucceeds()); -} - -// WNOWAIT allows the same wait result to be returned again. -TEST(WaitTest, WaitidWNOWAIT) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); - - siginfo_t info; - ASSERT_THAT(Waitid(P_PID, child, &info, WEXITED | WNOWAIT), - SyscallSucceeds()); - EXPECT_EQ(child, info.si_pid); - EXPECT_EQ(SIGCHLD, info.si_signo); - EXPECT_EQ(CLD_EXITED, info.si_code); - EXPECT_EQ(42, info.si_status); - - ASSERT_THAT(Waitid(P_PID, child, &info, WEXITED), SyscallSucceeds()); - EXPECT_EQ(child, info.si_pid); - EXPECT_EQ(SIGCHLD, info.si_signo); - EXPECT_EQ(CLD_EXITED, info.si_code); - EXPECT_EQ(42, info.si_status); - - EXPECT_THAT(Waitid(P_PID, child, &info, WEXITED), - SyscallFailsWithErrno(ECHILD)); -} - -// waitpid(pid, status, options) is equivalent to -// wait4(pid, status, options, nullptr). -// This is a dedicated syscall on i386, glibc maps it to wait4 on amd64. -TEST(WaitTest, WaitPid) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), - SyscallSucceedsWithValue(child)); - - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(42, WEXITSTATUS(status)); -} - -// Test that signaling a zombie succeeds. This is a signals test that is in this -// file for some reason. -TEST(WaitTest, KillZombie) { - pid_t child; - ASSERT_THAT(child = ForkAndExit(42, 0), SyscallSucceeds()); - - // Sleep for three seconds to ensure the child has exited. - absl::SleepFor(absl::Seconds(3)); - - // The child is now a zombie. Check that killing it returns 0. - EXPECT_THAT(kill(child, SIGTERM), SyscallSucceeds()); - EXPECT_THAT(kill(child, 0), SyscallSucceeds()); - - EXPECT_THAT(Wait4(child, nullptr, 0, nullptr), - SyscallSucceedsWithValue(child)); -} - -TEST(WaitTest, Wait4Rusage) { - pid_t child; - constexpr absl::Duration kSpin = absl::Seconds(3); - ASSERT_THAT(child = ForkSpinAndExit(21, absl::ToInt64Seconds(kSpin)), - SyscallSucceeds()); - - int status; - struct rusage rusage = {}; - ASSERT_THAT(Wait4(child, &status, 0, &rusage), - SyscallSucceedsWithValue(child)); - - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(21, WEXITSTATUS(status)); - - EXPECT_GE(RusageCpuTime(rusage), kSpin); -} - -TEST(WaitTest, WaitidRusage) { - pid_t child; - constexpr absl::Duration kSpin = absl::Seconds(3); - ASSERT_THAT(child = ForkSpinAndExit(27, absl::ToInt64Seconds(kSpin)), - SyscallSucceeds()); - - siginfo_t si = {}; - struct rusage rusage = {}; - - // From waitid(2): - // The raw waitid() system call takes a fifth argument, of type - // struct rusage *. If this argument is non-NULL, then it is used - // to return resource usage information about the child, in the - // same manner as wait4(2). - EXPECT_THAT( - RetryEINTR(syscall)(SYS_waitid, P_PID, child, &si, WEXITED, &rusage), - SyscallSucceeds()); - EXPECT_EQ(si.si_signo, SIGCHLD); - EXPECT_EQ(si.si_code, CLD_EXITED); - EXPECT_EQ(si.si_status, 27); - EXPECT_EQ(si.si_pid, child); - - EXPECT_GE(RusageCpuTime(rusage), kSpin); -} - -// After bf959931ddb88c4e4366e96dd22e68fa0db9527c ("wait/ptrace: assume __WALL -// if the child is traced") (Linux 4.7), tracees are always eligible for -// waiting, regardless of type. -TEST(WaitTest, TraceeWALL) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - FileDescriptor rfd(fds[0]); - FileDescriptor wfd(fds[1]); - - pid_t child = fork(); - if (child == 0) { - // Child. - rfd.reset(); - - TEST_PCHECK(ptrace(PTRACE_TRACEME, 0, nullptr, nullptr) == 0); - - // Notify parent that we're now a tracee. - wfd.reset(); - - _exit(0); - } - ASSERT_THAT(child, SyscallSucceeds()); - - wfd.reset(); - - // Wait for child to become tracee. - char c; - EXPECT_THAT(ReadFd(rfd.get(), &c, sizeof(c)), SyscallSucceedsWithValue(0)); - - // We can wait on the fork child with WCLONE, as it is a tracee. - int status; - if (IsRunningOnGvisor()) { - ASSERT_THAT(Wait4(child, &status, __WCLONE, nullptr), - SyscallSucceedsWithValue(child)); - - EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status; - } else { - // On older versions of Linux, we may get ECHILD. - ASSERT_THAT(Wait4(child, &status, __WCLONE, nullptr), - ::testing::AnyOf(SyscallSucceedsWithValue(child), - SyscallFailsWithErrno(ECHILD))); - } -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/write.cc b/test/syscalls/linux/write.cc deleted file mode 100644 index 3373ba72b..000000000 --- a/test/syscalls/linux/write.cc +++ /dev/null @@ -1,340 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <sys/mman.h> -#include <sys/resource.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> -#include <unistd.h> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/base/macros.h" -#include "test/util/cleanup.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// TODO(gvisor.dev/issue/2370): This test is currently very rudimentary. -class WriteTest : public ::testing::Test { - public: - ssize_t WriteBytes(int fd, int bytes) { - std::vector<char> buf(bytes); - std::fill(buf.begin(), buf.end(), 'a'); - return WriteFd(fd, buf.data(), buf.size()); - } -}; - -TEST_F(WriteTest, WriteNoExceedsRLimit) { - // Get the current rlimit and restore after test run. - struct rlimit initial_lim; - ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - auto cleanup = Cleanup([&initial_lim] { - EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - }); - - int fd; - struct rlimit setlim; - const int target_lim = 1024; - setlim.rlim_cur = target_lim; - setlim.rlim_max = RLIM_INFINITY; - const std::string pathname = NewTempAbsPath(); - ASSERT_THAT(fd = open(pathname.c_str(), O_WRONLY | O_CREAT, S_IRWXU), - SyscallSucceeds()); - ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); - - EXPECT_THAT(WriteBytes(fd, target_lim), SyscallSucceedsWithValue(target_lim)); - - std::vector<char> buf(target_lim + 1); - std::fill(buf.begin(), buf.end(), 'a'); - EXPECT_THAT(pwrite(fd, buf.data(), target_lim, 1), SyscallSucceeds()); - EXPECT_THAT(pwrite64(fd, buf.data(), target_lim, 1), SyscallSucceeds()); - - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(WriteTest, WriteExceedsRLimit) { - // Get the current rlimit and restore after test run. - struct rlimit initial_lim; - ASSERT_THAT(getrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - auto cleanup = Cleanup([&initial_lim] { - EXPECT_THAT(setrlimit(RLIMIT_FSIZE, &initial_lim), SyscallSucceeds()); - }); - - int fd; - sigset_t filesize_mask; - sigemptyset(&filesize_mask); - sigaddset(&filesize_mask, SIGXFSZ); - - struct rlimit setlim; - const int target_lim = 1024; - setlim.rlim_cur = target_lim; - setlim.rlim_max = RLIM_INFINITY; - - const std::string pathname = NewTempAbsPath(); - ASSERT_THAT(fd = open(pathname.c_str(), O_WRONLY | O_CREAT, S_IRWXU), - SyscallSucceeds()); - ASSERT_THAT(setrlimit(RLIMIT_FSIZE, &setlim), SyscallSucceeds()); - ASSERT_THAT(sigprocmask(SIG_BLOCK, &filesize_mask, nullptr), - SyscallSucceeds()); - std::vector<char> buf(target_lim + 2); - std::fill(buf.begin(), buf.end(), 'a'); - - EXPECT_THAT(write(fd, buf.data(), target_lim + 1), - SyscallSucceedsWithValue(target_lim)); - EXPECT_THAT(write(fd, buf.data(), 1), SyscallFailsWithErrno(EFBIG)); - siginfo_t info; - struct timespec timelimit = {0, 0}; - ASSERT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, &info, &timelimit), - SyscallSucceedsWithValue(SIGXFSZ)); - EXPECT_EQ(info.si_code, SI_USER); - EXPECT_EQ(info.si_pid, getpid()); - EXPECT_EQ(info.si_uid, getuid()); - - EXPECT_THAT(pwrite(fd, buf.data(), target_lim + 1, 1), - SyscallSucceedsWithValue(target_lim - 1)); - EXPECT_THAT(pwrite(fd, buf.data(), 1, target_lim), - SyscallFailsWithErrno(EFBIG)); - ASSERT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, &info, &timelimit), - SyscallSucceedsWithValue(SIGXFSZ)); - EXPECT_EQ(info.si_code, SI_USER); - EXPECT_EQ(info.si_pid, getpid()); - EXPECT_EQ(info.si_uid, getuid()); - - EXPECT_THAT(pwrite64(fd, buf.data(), target_lim + 1, 1), - SyscallSucceedsWithValue(target_lim - 1)); - EXPECT_THAT(pwrite64(fd, buf.data(), 1, target_lim), - SyscallFailsWithErrno(EFBIG)); - ASSERT_THAT(RetryEINTR(sigtimedwait)(&filesize_mask, &info, &timelimit), - SyscallSucceedsWithValue(SIGXFSZ)); - EXPECT_EQ(info.si_code, SI_USER); - EXPECT_EQ(info.si_pid, getpid()); - EXPECT_EQ(info.si_uid, getuid()); - - ASSERT_THAT(sigprocmask(SIG_UNBLOCK, &filesize_mask, nullptr), - SyscallSucceeds()); - EXPECT_THAT(close(fd), SyscallSucceeds()); -} - -TEST_F(WriteTest, WriteIncrementOffset) { - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); - int fd = f.get(); - - EXPECT_THAT(WriteBytes(fd, 0), SyscallSucceedsWithValue(0)); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - const int bytes_total = 1024; - - EXPECT_THAT(WriteBytes(fd, bytes_total), - SyscallSucceedsWithValue(bytes_total)); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(bytes_total)); -} - -TEST_F(WriteTest, WriteIncrementOffsetSeek) { - const std::string data = "hello world\n"; - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), data, TempPath::kDefaultFileMode)); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); - int fd = f.get(); - - const int seek_offset = data.size() / 2; - ASSERT_THAT(lseek(fd, seek_offset, SEEK_SET), - SyscallSucceedsWithValue(seek_offset)); - - const int write_bytes = 512; - EXPECT_THAT(WriteBytes(fd, write_bytes), - SyscallSucceedsWithValue(write_bytes)); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), - SyscallSucceedsWithValue(seek_offset + write_bytes)); -} - -TEST_F(WriteTest, WriteIncrementOffsetAppend) { - const std::string data = "hello world\n"; - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), data, TempPath::kDefaultFileMode)); - FileDescriptor f = ASSERT_NO_ERRNO_AND_VALUE( - Open(tmpfile.path().c_str(), O_WRONLY | O_APPEND)); - int fd = f.get(); - - EXPECT_THAT(WriteBytes(fd, 1024), SyscallSucceedsWithValue(1024)); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), - SyscallSucceedsWithValue(data.size() + 1024)); -} - -TEST_F(WriteTest, WriteIncrementOffsetEOF) { - const std::string data = "hello world\n"; - const TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), data, TempPath::kDefaultFileMode)); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); - int fd = f.get(); - - EXPECT_THAT(lseek(fd, 0, SEEK_END), SyscallSucceedsWithValue(data.size())); - - EXPECT_THAT(WriteBytes(fd, 1024), SyscallSucceedsWithValue(1024)); - EXPECT_THAT(lseek(fd, 0, SEEK_END), - SyscallSucceedsWithValue(data.size() + 1024)); -} - -TEST_F(WriteTest, PwriteNoChangeOffset) { - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_WRONLY)); - int fd = f.get(); - - const std::string data = "hello world\n"; - - EXPECT_THAT(pwrite(fd, data.data(), data.size(), 0), - SyscallSucceedsWithValue(data.size())); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(0)); - - const int bytes_total = 1024; - ASSERT_THAT(WriteBytes(fd, bytes_total), - SyscallSucceedsWithValue(bytes_total)); - ASSERT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(bytes_total)); - - EXPECT_THAT(pwrite(fd, data.data(), data.size(), bytes_total), - SyscallSucceedsWithValue(data.size())); - EXPECT_THAT(lseek(fd, 0, SEEK_CUR), SyscallSucceedsWithValue(bytes_total)); -} - -TEST_F(WriteTest, WriteWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); - int fd = f.get(); - - EXPECT_THAT(WriteBytes(fd, 1024), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(WriteTest, WritevWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); - int fd = f.get(); - - char buf[16]; - struct iovec iov; - iov.iov_base = buf; - iov.iov_len = sizeof(buf); - - EXPECT_THAT(writev(fd, &iov, /*__count=*/1), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(WriteTest, PwriteWithOpath) { - SKIP_IF(IsRunningWithVFS1()); - TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor f = - ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); - int fd = f.get(); - - const std::string data = "hello world\n"; - - EXPECT_THAT(pwrite(fd, data.data(), data.size(), 0), - SyscallFailsWithErrno(EBADF)); -} - -// Test that partial writes that hit SIGSEGV are correctly handled and return -// partial write. -TEST_F(WriteTest, PartialWriteSIGSEGV) { - // Allocate 2 pages and remove permission from the second. - const size_t size = 2 * kPageSize; - void* addr = mmap(0, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0); - ASSERT_NE(addr, MAP_FAILED); - auto cleanup = Cleanup( - [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); - - void* badAddr = reinterpret_cast<char*>(addr) + kPageSize; - ASSERT_THAT(mprotect(badAddr, kPageSize, PROT_NONE), SyscallSucceeds()); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_WRONLY)); - - // Attempt to write both pages to the file. Create a non-contiguous iovec pair - // to ensure operation is done in 2 steps. - struct iovec iov[] = { - { - .iov_base = addr, - .iov_len = kPageSize, - }, - { - .iov_base = addr, - .iov_len = size, - }, - }; - // Write should succeed for the first iovec and half of the second (=2 pages). - EXPECT_THAT(pwritev(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), - SyscallSucceedsWithValue(2 * kPageSize)); -} - -// Test that partial writes that hit SIGBUS are correctly handled and return -// partial write. -TEST_F(WriteTest, PartialWriteSIGBUS) { - SKIP_IF(getenv("GVISOR_GOFER_UNCACHED")); // Can't mmap from uncached files. - - TempPath mapfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd_map = - ASSERT_NO_ERRNO_AND_VALUE(Open(mapfile.path().c_str(), O_RDWR)); - - // Let the first page be read to force a partial write. - ASSERT_THAT(ftruncate(fd_map.get(), kPageSize), SyscallSucceeds()); - - // Map 2 pages, one of which is not allocated in the backing file. Reading - // from it will trigger a SIGBUS. - const size_t size = 2 * kPageSize; - void* addr = - mmap(NULL, size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd_map.get(), 0); - ASSERT_NE(addr, MAP_FAILED); - auto cleanup = Cleanup( - [addr, size] { EXPECT_THAT(munmap(addr, size), SyscallSucceeds()); }); - - TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_WRONLY)); - - // Attempt to write both pages to the file. Create a non-contiguous iovec pair - // to ensure operation is done in 2 steps. - struct iovec iov[] = { - { - .iov_base = addr, - .iov_len = kPageSize, - }, - { - .iov_base = addr, - .iov_len = size, - }, - }; - // Write should succeed for the first iovec and half of the second (=2 pages). - ASSERT_THAT(pwritev(fd.get(), iov, ABSL_ARRAYSIZE(iov), 0), - SyscallSucceedsWithValue(2 * kPageSize)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc deleted file mode 100644 index a953a55fe..000000000 --- a/test/syscalls/linux/xattr.cc +++ /dev/null @@ -1,711 +0,0 @@ -// Copyright 2019 The gVisor Authors. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include <errno.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/xattr.h> -#include <unistd.h> - -#include <string> -#include <vector> - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "absl/container/flat_hash_set.h" -#include "test/syscalls/linux/file_base.h" -#include "test/util/capability_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -using ::gvisor::testing::IsTmpfs; - -class XattrTest : public FileTest {}; - -TEST_F(XattrTest, XattrNonexistentFile) { - const char* path = "/does/not/exist"; - const char* name = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(ENOENT)); - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENOENT)); - EXPECT_THAT(listxattr(path, nullptr, 0), SyscallFailsWithErrno(ENOENT)); - EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(ENOENT)); -} - -TEST_F(XattrTest, XattrNullName) { - const char* path = test_file_name_.c_str(); - - EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(EFAULT)); - EXPECT_THAT(getxattr(path, nullptr, nullptr, 0), - SyscallFailsWithErrno(EFAULT)); - EXPECT_THAT(removexattr(path, nullptr), SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(XattrTest, XattrEmptyName) { - const char* path = test_file_name_.c_str(); - - EXPECT_THAT(setxattr(path, "", nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(ERANGE)); - EXPECT_THAT(getxattr(path, "", nullptr, 0), SyscallFailsWithErrno(ERANGE)); - EXPECT_THAT(removexattr(path, ""), SyscallFailsWithErrno(ERANGE)); -} - -TEST_F(XattrTest, XattrLargeName) { - const char* path = test_file_name_.c_str(); - std::string name = "user."; - name += std::string(XATTR_NAME_MAX - name.length(), 'a'); - - if (!IsRunningOnGvisor()) { - // In gVisor, access to xattrs is controlled with an explicit list of - // allowed names. This name isn't going to be configured to allow access, so - // don't test it. - EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0), - SyscallSucceedsWithValue(0)); - } - - name += "a"; - EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(ERANGE)); - EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0), - SyscallFailsWithErrno(ERANGE)); - EXPECT_THAT(removexattr(path, name.c_str()), SyscallFailsWithErrno(ERANGE)); -} - -TEST_F(XattrTest, XattrInvalidPrefix) { - const char* path = test_file_name_.c_str(); - std::string name(XATTR_NAME_MAX, 'a'); - EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(EOPNOTSUPP)); - EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0), - SyscallFailsWithErrno(EOPNOTSUPP)); - EXPECT_THAT(removexattr(path, name.c_str()), - SyscallFailsWithErrno(EOPNOTSUPP)); -} - -// Do not allow save/restore cycles after making the test file read-only, as -// the restore will fail to open it with r/w permissions. -TEST_F(XattrTest, XattrReadOnly_NoRandomSave) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - size_t size = sizeof(val); - - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - - DisableSave ds; - ASSERT_NO_ERRNO(testing::Chmod(test_file_name_, S_IRUSR)); - - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), - SyscallFailsWithErrno(EACCES)); - EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EACCES)); - - char buf = '-'; - EXPECT_THAT(getxattr(path, name, &buf, size), SyscallSucceedsWithValue(size)); - EXPECT_EQ(buf, val); - - char list[sizeof(name)]; - EXPECT_THAT(listxattr(path, list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); -} - -// Do not allow save/restore cycles after making the test file write-only, as -// the restore will fail to open it with r/w permissions. -TEST_F(XattrTest, XattrWriteOnly_NoRandomSave) { - // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); - - DisableSave ds; - ASSERT_NO_ERRNO(testing::Chmod(test_file_name_, S_IWUSR)); - - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - size_t size = sizeof(val); - - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(EACCES)); - - // listxattr will succeed even without read permissions. - char list[sizeof(name)]; - EXPECT_THAT(listxattr(path, list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); - - EXPECT_THAT(removexattr(path, name), SyscallSucceeds()); -} - -TEST_F(XattrTest, XattrTrustedWithNonadmin) { - // TODO(b/148380782): Support setxattr and getxattr with "trusted" prefix. - SKIP_IF(IsRunningOnGvisor()); - SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); - - const char* path = test_file_name_.c_str(); - const char name[] = "trusted.abc"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, XattrOnDirectory) { - TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(dir.path().c_str(), name, nullptr, 0, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(getxattr(dir.path().c_str(), name, nullptr, 0), - SyscallSucceedsWithValue(0)); - - char list[sizeof(name)]; - EXPECT_THAT(listxattr(dir.path().c_str(), list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); - - EXPECT_THAT(removexattr(dir.path().c_str(), name), SyscallSucceeds()); -} - -TEST_F(XattrTest, XattrOnSymlink) { - TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(dir.path(), test_file_name_)); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(link.path().c_str(), name, nullptr, 0, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(getxattr(link.path().c_str(), name, nullptr, 0), - SyscallSucceedsWithValue(0)); - - char list[sizeof(name)]; - EXPECT_THAT(listxattr(link.path().c_str(), list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); - - EXPECT_THAT(removexattr(link.path().c_str(), name), SyscallSucceeds()); -} - -TEST_F(XattrTest, XattrOnInvalidFileTypes) { - const char name[] = "user.test"; - - char char_device[] = "/dev/zero"; - EXPECT_THAT(setxattr(char_device, name, nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(getxattr(char_device, name, nullptr, 0), - SyscallFailsWithErrno(ENODATA)); - EXPECT_THAT(listxattr(char_device, nullptr, 0), SyscallSucceedsWithValue(0)); - - // Use tmpfs, where creation of named pipes is supported. - const std::string fifo = NewTempAbsPathInDir("/dev/shm"); - const char* path = fifo.c_str(); - EXPECT_THAT(mknod(path, S_IFIFO | S_IRUSR | S_IWUSR, 0), SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); - EXPECT_THAT(listxattr(path, nullptr, 0), SyscallSucceedsWithValue(0)); - EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EPERM)); -} - -TEST_F(XattrTest, SetXattrSizeSmallerThanValue) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - std::vector<char> val = {'a', 'a'}; - size_t size = 1; - EXPECT_THAT(setxattr(path, name, val.data(), size, /*flags=*/0), - SyscallSucceeds()); - - std::vector<char> buf = {'-', '-'}; - std::vector<char> expected_buf = {'a', '-'}; - EXPECT_THAT(getxattr(path, name, buf.data(), buf.size()), - SyscallSucceedsWithValue(size)); - EXPECT_EQ(buf, expected_buf); -} - -TEST_F(XattrTest, SetXattrZeroSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - EXPECT_THAT(setxattr(path, name, &val, 0, /*flags=*/0), SyscallSucceeds()); - - char buf = '-'; - EXPECT_THAT(getxattr(path, name, &buf, XATTR_SIZE_MAX), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(buf, '-'); -} - -TEST_F(XattrTest, SetXattrSizeTooLarge) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - - // Note that each particular fs implementation may stipulate a lower size - // limit, in which case we actually may fail (e.g. error with ENOSPC) for - // some sizes under XATTR_SIZE_MAX. - size_t size = XATTR_SIZE_MAX + 1; - std::vector<char> val(size); - EXPECT_THAT(setxattr(path, name, val.data(), size, /*flags=*/0), - SyscallFailsWithErrno(E2BIG)); - - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, SetXattrNullValueAndNonzeroSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 1, /*flags=*/0), - SyscallFailsWithErrno(EFAULT)); - - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, SetXattrNullValueAndZeroSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallSucceedsWithValue(0)); -} - -TEST_F(XattrTest, SetXattrValueTooLargeButOKSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - std::vector<char> val(XATTR_SIZE_MAX + 1); - std::fill(val.begin(), val.end(), 'a'); - size_t size = 1; - EXPECT_THAT(setxattr(path, name, val.data(), size, /*flags=*/0), - SyscallSucceeds()); - - std::vector<char> buf = {'-', '-'}; - std::vector<char> expected_buf = {'a', '-'}; - EXPECT_THAT(getxattr(path, name, buf.data(), size), - SyscallSucceedsWithValue(size)); - EXPECT_EQ(buf, expected_buf); -} - -TEST_F(XattrTest, SetXattrReplaceWithSmaller) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - std::vector<char> val = {'a', 'a'}; - EXPECT_THAT(setxattr(path, name, val.data(), 2, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name, val.data(), 1, /*flags=*/0), - SyscallSucceeds()); - - std::vector<char> buf = {'-', '-'}; - std::vector<char> expected_buf = {'a', '-'}; - EXPECT_THAT(getxattr(path, name, buf.data(), 2), SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, expected_buf); -} - -TEST_F(XattrTest, SetXattrReplaceWithLarger) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - std::vector<char> val = {'a', 'a'}; - EXPECT_THAT(setxattr(path, name, val.data(), 1, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name, val.data(), 2, /*flags=*/0), - SyscallSucceeds()); - - std::vector<char> buf = {'-', '-'}; - EXPECT_THAT(getxattr(path, name, buf.data(), 2), SyscallSucceedsWithValue(2)); - EXPECT_EQ(buf, val); -} - -TEST_F(XattrTest, SetXattrCreateFlag) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_CREATE), - SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_CREATE), - SyscallFailsWithErrno(EEXIST)); - - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallSucceedsWithValue(0)); -} - -TEST_F(XattrTest, SetXattrReplaceFlag) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_REPLACE), - SyscallFailsWithErrno(ENODATA)); - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_REPLACE), - SyscallSucceeds()); - - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallSucceedsWithValue(0)); -} - -TEST_F(XattrTest, SetXattrInvalidFlags) { - const char* path = test_file_name_.c_str(); - int invalid_flags = 0xff; - EXPECT_THAT(setxattr(path, nullptr, nullptr, 0, invalid_flags), - SyscallFailsWithErrno(EINVAL)); -} - -TEST_F(XattrTest, GetXattr) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - int val = 1234; - size_t size = sizeof(val); - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - - int buf = 0; - EXPECT_THAT(getxattr(path, name, &buf, size), SyscallSucceedsWithValue(size)); - EXPECT_EQ(buf, val); -} - -TEST_F(XattrTest, GetXattrSizeSmallerThanValue) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - std::vector<char> val = {'a', 'a'}; - size_t size = val.size(); - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - - char buf = '-'; - EXPECT_THAT(getxattr(path, name, &buf, 1), SyscallFailsWithErrno(ERANGE)); - EXPECT_EQ(buf, '-'); -} - -TEST_F(XattrTest, GetXattrSizeLargerThanValue) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - EXPECT_THAT(setxattr(path, name, &val, 1, /*flags=*/0), SyscallSucceeds()); - - std::vector<char> buf(XATTR_SIZE_MAX); - std::fill(buf.begin(), buf.end(), '-'); - std::vector<char> expected_buf = buf; - expected_buf[0] = 'a'; - EXPECT_THAT(getxattr(path, name, buf.data(), buf.size()), - SyscallSucceedsWithValue(1)); - EXPECT_EQ(buf, expected_buf); -} - -TEST_F(XattrTest, GetXattrZeroSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - EXPECT_THAT(setxattr(path, name, &val, sizeof(val), /*flags=*/0), - SyscallSucceeds()); - - char buf = '-'; - EXPECT_THAT(getxattr(path, name, &buf, 0), - SyscallSucceedsWithValue(sizeof(val))); - EXPECT_EQ(buf, '-'); -} - -TEST_F(XattrTest, GetXattrSizeTooLarge) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - EXPECT_THAT(setxattr(path, name, &val, sizeof(val), /*flags=*/0), - SyscallSucceeds()); - - std::vector<char> buf(XATTR_SIZE_MAX + 1); - std::fill(buf.begin(), buf.end(), '-'); - std::vector<char> expected_buf = buf; - expected_buf[0] = 'a'; - EXPECT_THAT(getxattr(path, name, buf.data(), buf.size()), - SyscallSucceedsWithValue(sizeof(val))); - EXPECT_EQ(buf, expected_buf); -} - -TEST_F(XattrTest, GetXattrNullValue) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - size_t size = sizeof(val); - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - - EXPECT_THAT(getxattr(path, name, nullptr, size), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(XattrTest, GetXattrNullValueAndZeroSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - char val = 'a'; - size_t size = sizeof(val); - // Set value with zero size. - EXPECT_THAT(setxattr(path, name, &val, 0, /*flags=*/0), SyscallSucceeds()); - // Get value with nonzero size. - EXPECT_THAT(getxattr(path, name, nullptr, size), SyscallSucceedsWithValue(0)); - - // Set value with nonzero size. - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - // Get value with zero size. - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallSucceedsWithValue(size)); -} - -TEST_F(XattrTest, GetXattrNonexistentName) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, ListXattr) { - const char* path = test_file_name_.c_str(); - const std::string name = "user.test"; - const std::string name2 = "user.test2"; - const std::string name3 = "user.test3"; - EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name2.c_str(), nullptr, 0, /*flags=*/0), - SyscallSucceeds()); - EXPECT_THAT(setxattr(path, name3.c_str(), nullptr, 0, /*flags=*/0), - SyscallSucceeds()); - - std::vector<char> list(name.size() + 1 + name2.size() + 1 + name3.size() + 1); - char* buf = list.data(); - EXPECT_THAT(listxattr(path, buf, XATTR_SIZE_MAX), - SyscallSucceedsWithValue(list.size())); - - absl::flat_hash_set<std::string> got = {}; - for (char* p = buf; p < buf + list.size(); p += strlen(p) + 1) { - got.insert(std::string{p}); - } - - absl::flat_hash_set<std::string> expected = {name, name2, name3}; - EXPECT_EQ(got, expected); -} - -TEST_F(XattrTest, ListXattrNoXattrs) { - const char* path = test_file_name_.c_str(); - - std::vector<char> list, expected; - EXPECT_THAT(listxattr(path, list.data(), sizeof(list)), - SyscallSucceedsWithValue(0)); - EXPECT_EQ(list, expected); - - // ListXattr should succeed if there are no attributes, even if the buffer - // passed in is a nullptr. - EXPECT_THAT(listxattr(path, nullptr, sizeof(list)), - SyscallSucceedsWithValue(0)); -} - -TEST_F(XattrTest, ListXattrNullBuffer) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - - EXPECT_THAT(listxattr(path, nullptr, sizeof(name)), - SyscallFailsWithErrno(EFAULT)); -} - -TEST_F(XattrTest, ListXattrSizeTooSmall) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - - char list[sizeof(name) - 1]; - EXPECT_THAT(listxattr(path, list, sizeof(list)), - SyscallFailsWithErrno(ERANGE)); -} - -TEST_F(XattrTest, ListXattrZeroSize) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - EXPECT_THAT(listxattr(path, nullptr, 0), - SyscallSucceedsWithValue(sizeof(name))); -} - -TEST_F(XattrTest, RemoveXattr) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(setxattr(path, name, nullptr, 0, /*flags=*/0), SyscallSucceeds()); - EXPECT_THAT(removexattr(path, name), SyscallSucceeds()); - EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, RemoveXattrNonexistentName) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, LXattrOnSymlink) { - const char name[] = "user.test"; - TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - TempPath link = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(dir.path(), test_file_name_)); - - EXPECT_THAT(lsetxattr(link.path().c_str(), name, nullptr, 0, 0), - SyscallFailsWithErrno(EPERM)); - EXPECT_THAT(lgetxattr(link.path().c_str(), name, nullptr, 0), - SyscallFailsWithErrno(ENODATA)); - EXPECT_THAT(llistxattr(link.path().c_str(), nullptr, 0), - SyscallSucceedsWithValue(0)); - EXPECT_THAT(lremovexattr(link.path().c_str(), name), - SyscallFailsWithErrno(EPERM)); -} - -TEST_F(XattrTest, LXattrOnNonsymlink) { - const char* path = test_file_name_.c_str(); - const char name[] = "user.test"; - int val = 1234; - size_t size = sizeof(val); - EXPECT_THAT(lsetxattr(path, name, &val, size, /*flags=*/0), - SyscallSucceeds()); - - int buf = 0; - EXPECT_THAT(lgetxattr(path, name, &buf, size), - SyscallSucceedsWithValue(size)); - EXPECT_EQ(buf, val); - - char list[sizeof(name)]; - EXPECT_THAT(llistxattr(path, list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); - - EXPECT_THAT(lremovexattr(path, name), SyscallSucceeds()); -} - -TEST_F(XattrTest, XattrWithFD) { - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_.c_str(), 0)); - const char name[] = "user.test"; - int val = 1234; - size_t size = sizeof(val); - EXPECT_THAT(fsetxattr(fd.get(), name, &val, size, /*flags=*/0), - SyscallSucceeds()); - - int buf = 0; - EXPECT_THAT(fgetxattr(fd.get(), name, &buf, size), - SyscallSucceedsWithValue(size)); - EXPECT_EQ(buf, val); - - char list[sizeof(name)]; - EXPECT_THAT(flistxattr(fd.get(), list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); - - EXPECT_THAT(fremovexattr(fd.get(), name), SyscallSucceeds()); -} - -TEST_F(XattrTest, XattrWithOPath) { - SKIP_IF(IsRunningWithVFS1()); - const FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_.c_str(), O_PATH)); - const char name[] = "user.test"; - int val = 1234; - size_t size = sizeof(val); - EXPECT_THAT(fsetxattr(fd.get(), name, &val, size, /*flags=*/0), - SyscallFailsWithErrno(EBADF)); - - int buf; - EXPECT_THAT(fgetxattr(fd.get(), name, &buf, size), - SyscallFailsWithErrno(EBADF)); - - char list[sizeof(name)]; - EXPECT_THAT(flistxattr(fd.get(), list, sizeof(list)), - SyscallFailsWithErrno(EBADF)); - - EXPECT_THAT(fremovexattr(fd.get(), name), SyscallFailsWithErrno(EBADF)); -} - -TEST_F(XattrTest, TrustedNamespaceWithCapSysAdmin) { - // Trusted namespace not supported in VFS1. - SKIP_IF(IsRunningWithVFS1()); - - // TODO(b/66162845): Only gVisor tmpfs currently supports trusted namespace. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(test_file_name_))); - - const char* path = test_file_name_.c_str(); - const char name[] = "trusted.test"; - - // Writing to the trusted.* xattr namespace requires CAP_SYS_ADMIN in the root - // user namespace. There's no easy way to check that, other than trying the - // operation and seeing what happens. We'll call removexattr because it's - // simplest. - if (removexattr(path, name) < 0) { - SKIP_IF(errno == EPERM); - FAIL() << "unexpected errno from removexattr: " << errno; - } - - // Set. - char val = 'a'; - size_t size = sizeof(val); - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds()); - - // Get. - char got = '\0'; - EXPECT_THAT(getxattr(path, name, &got, size), SyscallSucceedsWithValue(size)); - EXPECT_EQ(val, got); - - // List. - char list[sizeof(name)]; - EXPECT_THAT(listxattr(path, list, sizeof(list)), - SyscallSucceedsWithValue(sizeof(name))); - EXPECT_STREQ(list, name); - - // Remove. - EXPECT_THAT(removexattr(path, name), SyscallSucceeds()); - - // Get should now return ENODATA. - EXPECT_THAT(getxattr(path, name, &got, size), SyscallFailsWithErrno(ENODATA)); -} - -TEST_F(XattrTest, TrustedNamespaceWithoutCapSysAdmin) { - // Trusted namespace not supported in VFS1. - SKIP_IF(IsRunningWithVFS1()); - - // TODO(b/66162845): Only gVisor tmpfs currently supports trusted namespace. - SKIP_IF(IsRunningOnGvisor() && - !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(test_file_name_))); - - // Drop CAP_SYS_ADMIN if we have it. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); - } - - const char* path = test_file_name_.c_str(); - const char name[] = "trusted.test"; - - // Set fails. - char val = 'a'; - size_t size = sizeof(val); - EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), - SyscallFailsWithErrno(EPERM)); - - // Get fails. - char got = '\0'; - EXPECT_THAT(getxattr(path, name, &got, size), SyscallFailsWithErrno(ENODATA)); - - // List still works, but returns no items. - char list[sizeof(name)]; - EXPECT_THAT(listxattr(path, list, sizeof(list)), SyscallSucceedsWithValue(0)); - - // Remove fails. - EXPECT_THAT(removexattr(path, name), SyscallFailsWithErrno(EPERM)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor |