summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/BUILD45
-rw-r--r--test/e2e/BUILD5
-rw-r--r--test/image/BUILD5
-rw-r--r--test/iptables/BUILD11
-rw-r--r--test/iptables/README.md4
-rw-r--r--test/iptables/filter_input.go130
-rw-r--r--test/iptables/filter_output.go87
-rw-r--r--test/iptables/iptables_test.go54
-rw-r--r--test/iptables/iptables_util.go50
-rw-r--r--test/iptables/nat.go80
-rw-r--r--test/iptables/runner/BUILD13
-rw-r--r--test/root/BUILD5
-rw-r--r--test/root/testdata/BUILD3
-rw-r--r--test/runtimes/BUILD4
-rw-r--r--test/runtimes/build_defs.bzl5
-rw-r--r--test/runtimes/images/proctor/BUILD4
-rw-r--r--test/syscalls/BUILD9
-rw-r--r--test/syscalls/build_defs.bzl6
-rw-r--r--test/syscalls/gtest/BUILD7
-rw-r--r--test/syscalls/linux/32bit.cc132
-rw-r--r--test/syscalls/linux/BUILD108
-rw-r--r--test/syscalls/linux/bad.cc15
-rw-r--r--test/syscalls/linux/concurrency.cc2
-rw-r--r--test/syscalls/linux/connect_external.cc12
-rw-r--r--test/syscalls/linux/eventfd.cc25
-rw-r--r--test/syscalls/linux/exceptions.cc2
-rw-r--r--test/syscalls/linux/fault.cc3
-rw-r--r--test/syscalls/linux/fcntl.cc5
-rw-r--r--test/syscalls/linux/fpsig_fork.cc4
-rw-r--r--test/syscalls/linux/fpsig_nested.cc8
-rw-r--r--test/syscalls/linux/getdents.cc5
-rw-r--r--test/syscalls/linux/getrusage.cc2
-rw-r--r--test/syscalls/linux/inotify.cc32
-rw-r--r--test/syscalls/linux/ip_socket_test_util.cc10
-rw-r--r--test/syscalls/linux/ip_socket_test_util.h6
-rw-r--r--test/syscalls/linux/iptables.h2
-rw-r--r--test/syscalls/linux/link.cc3
-rw-r--r--test/syscalls/linux/madvise.cc12
-rw-r--r--test/syscalls/linux/mempolicy.cc10
-rw-r--r--test/syscalls/linux/mlock.cc1
-rw-r--r--test/syscalls/linux/msync.cc4
-rw-r--r--test/syscalls/linux/partial_bad_buffer.cc138
-rw-r--r--test/syscalls/linux/pipe.cc6
-rw-r--r--test/syscalls/linux/poll.cc3
-rw-r--r--test/syscalls/linux/preadv2.cc2
-rw-r--r--test/syscalls/linux/proc.cc58
-rw-r--r--test/syscalls/linux/proc_net.cc20
-rw-r--r--test/syscalls/linux/ptrace.cc13
-rw-r--r--test/syscalls/linux/readv_common.cc2
-rw-r--r--test/syscalls/linux/rseq/BUILD5
-rw-r--r--test/syscalls/linux/seccomp.cc13
-rw-r--r--test/syscalls/linux/sendfile_socket.cc8
-rw-r--r--test/syscalls/linux/sigaction.cc53
-rw-r--r--test/syscalls/linux/sigaltstack.cc6
-rw-r--r--test/syscalls/linux/sigiret.cc4
-rw-r--r--test/syscalls/linux/signalfd.cc118
-rw-r--r--test/syscalls/linux/socket_abstract.cc2
-rw-r--r--test/syscalls/linux/socket_bind_to_device_distribution.cc25
-rw-r--r--test/syscalls/linux/socket_bind_to_device_sequence.cc10
-rw-r--r--test/syscalls/linux/socket_filesystem.cc2
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc343
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.cc33
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic_loopback.cc2
-rw-r--r--test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc2
-rw-r--r--test/syscalls/linux/socket_ip_udp_generic.cc40
-rw-r--r--test/syscalls/linux/socket_ip_udp_loopback.cc2
-rw-r--r--test/syscalls/linux/socket_ip_udp_loopback_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc2
-rw-r--r--test/syscalls/linux/socket_ip_unbound.cc33
-rw-r--r--test/syscalls/linux/socket_netdevice.cc10
-rw-r--r--test/syscalls/linux/socket_netlink_route.cc84
-rw-r--r--test/syscalls/linux/socket_netlink_util.cc38
-rw-r--r--test/syscalls/linux/socket_netlink_util.h11
-rw-r--r--test/syscalls/linux/socket_non_stream.cc113
-rw-r--r--test/syscalls/linux/socket_non_stream_blocking.cc37
-rw-r--r--test/syscalls/linux/socket_stream.cc55
-rw-r--r--test/syscalls/linux/socket_stream_blocking.cc64
-rw-r--r--test/syscalls/linux/socket_unix_abstract_nonblock.cc2
-rw-r--r--test/syscalls/linux/socket_unix_blocking_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_dgram_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_domain.cc2
-rw-r--r--test/syscalls/linux/socket_unix_filesystem_nonblock.cc2
-rw-r--r--test/syscalls/linux/socket_unix_non_stream_blocking_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_pair.cc2
-rw-r--r--test/syscalls/linux/socket_unix_pair_nonblock.cc2
-rw-r--r--test/syscalls/linux/socket_unix_seqpacket_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_stream_blocking_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_stream_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_stream_nonblock_local.cc2
-rw-r--r--test/syscalls/linux/splice.cc56
-rw-r--r--test/syscalls/linux/stat.cc4
-rw-r--r--test/syscalls/linux/tcp_socket.cc67
-rw-r--r--test/syscalls/linux/udp_socket_errqueue_test_case.cc5
-rw-r--r--test/syscalls/linux/udp_socket_test_cases.cc8
-rw-r--r--test/syscalls/linux/utimes.cc21
-rw-r--r--test/syscalls/linux/xattr.cc191
-rw-r--r--test/uds/BUILD3
-rw-r--r--test/util/BUILD32
-rw-r--r--test/util/capability_util.cc8
-rw-r--r--test/util/fs_util.cc2
-rw-r--r--test/util/fs_util.h11
-rw-r--r--test/util/mount_util.h8
-rw-r--r--test/util/multiprocess_util.h6
-rw-r--r--test/util/platform_util.cc49
-rw-r--r--test/util/platform_util.h56
-rw-r--r--test/util/save_util_linux.cc18
-rw-r--r--test/util/save_util_other.cc4
-rw-r--r--test/util/test_util.cc11
-rw-r--r--test/util/test_util.h27
-rw-r--r--test/util/test_util_runfiles.cc4
111 files changed, 2214 insertions, 692 deletions
diff --git a/test/BUILD b/test/BUILD
index 01fa01f2e..34b950644 100644
--- a/test/BUILD
+++ b/test/BUILD
@@ -1,44 +1 @@
-package(licenses = ["notice"]) # Apache 2.0
-
-# We need to define a bazel platform and toolchain to specify dockerPrivileged
-# and dockerRunAsRoot options, they are required to run tests on the RBE
-# cluster in Kokoro.
-alias(
- name = "rbe_ubuntu1604",
- actual = ":rbe_ubuntu1604_r346485",
-)
-
-platform(
- name = "rbe_ubuntu1604_r346485",
- constraint_values = [
- "@bazel_tools//platforms:x86_64",
- "@bazel_tools//platforms:linux",
- "@bazel_tools//tools/cpp:clang",
- "@bazel_toolchains//constraints:xenial",
- "@bazel_toolchains//constraints/sanitizers:support_msan",
- ],
- remote_execution_properties = """
- properties: {
- name: "container-image"
- value:"docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:69c9f1652941d64a46f6f7358a44c1718f25caa5cb1ced4a58ccc5281cd183b5"
- }
- properties: {
- name: "dockerAddCapabilities"
- value: "SYS_ADMIN"
- }
- properties: {
- name: "dockerPrivileged"
- value: "true"
- }
- """,
-)
-
-toolchain(
- name = "cc-toolchain-clang-x86_64-default",
- exec_compatible_with = [
- ],
- target_compatible_with = [
- ],
- toolchain = "@bazel_toolchains//configs/ubuntu16_04_clang/9.0.0/bazel_0.28.0/cc:cc-compiler-k8",
- toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
-)
+package(licenses = ["notice"])
diff --git a/test/e2e/BUILD b/test/e2e/BUILD
index 4fe03a220..76e04f878 100644
--- a/test/e2e/BUILD
+++ b/test/e2e/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
@@ -10,7 +10,7 @@ go_test(
"integration_test.go",
"regression_test.go",
],
- embed = [":integration"],
+ library = ":integration",
tags = [
# Requires docker and runsc to be configured before the test runs.
"manual",
@@ -29,5 +29,4 @@ go_test(
go_library(
name = "integration",
srcs = ["integration.go"],
- importpath = "gvisor.dev/gvisor/test/integration",
)
diff --git a/test/image/BUILD b/test/image/BUILD
index 09b0a0ad5..7392ac54e 100644
--- a/test/image/BUILD
+++ b/test/image/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
@@ -14,7 +14,7 @@ go_test(
"ruby.rb",
"ruby.sh",
],
- embed = [":image"],
+ library = ":image",
tags = [
# Requires docker and runsc to be configured before the test runs.
"manual",
@@ -30,5 +30,4 @@ go_test(
go_library(
name = "image",
srcs = ["image.go"],
- importpath = "gvisor.dev/gvisor/test/image",
)
diff --git a/test/iptables/BUILD b/test/iptables/BUILD
index fa833c3b2..6bb3b82b5 100644
--- a/test/iptables/BUILD
+++ b/test/iptables/BUILD
@@ -1,16 +1,21 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
go_library(
name = "iptables",
+ testonly = 1,
srcs = [
"filter_input.go",
+ "filter_output.go",
"iptables.go",
"iptables_util.go",
+ "nat.go",
],
- importpath = "gvisor.dev/gvisor/test/iptables",
visibility = ["//test/iptables:__subpackages__"],
+ deps = [
+ "//runsc/testutil",
+ ],
)
go_test(
@@ -18,7 +23,7 @@ go_test(
srcs = [
"iptables_test.go",
],
- embed = [":iptables"],
+ library = ":iptables",
tags = [
"local",
"manual",
diff --git a/test/iptables/README.md b/test/iptables/README.md
index b37cb2a96..8f61b4c41 100644
--- a/test/iptables/README.md
+++ b/test/iptables/README.md
@@ -1,6 +1,6 @@
# iptables Tests
-iptables tests are run via `scripts/iptables\_test.sh`.
+iptables tests are run via `scripts/iptables_test.sh`.
## Test Structure
@@ -28,7 +28,7 @@ Your test is now runnable with bazel!
Build the testing Docker container:
```bash
-$ bazel run //test/iptables/runner -- --norun
+$ bazel run //test/iptables/runner-image -- --norun
```
Run an individual test via:
diff --git a/test/iptables/filter_input.go b/test/iptables/filter_input.go
index 923f44e68..fd02ff2ff 100644
--- a/test/iptables/filter_input.go
+++ b/test/iptables/filter_input.go
@@ -28,9 +28,13 @@ const (
)
func init() {
- RegisterTestCase(FilterInputDropUDP{})
- RegisterTestCase(FilterInputDropUDPPort{})
+ RegisterTestCase(FilterInputDropAll{})
RegisterTestCase(FilterInputDropDifferentUDPPort{})
+ RegisterTestCase(FilterInputDropOnlyUDP{})
+ RegisterTestCase(FilterInputDropTCPDestPort{})
+ RegisterTestCase(FilterInputDropTCPSrcPort{})
+ RegisterTestCase(FilterInputDropUDPPort{})
+ RegisterTestCase(FilterInputDropUDP{})
}
// FilterInputDropUDP tests that we can drop UDP traffic.
@@ -64,6 +68,35 @@ func (FilterInputDropUDP) LocalAction(ip net.IP) error {
return sendUDPLoop(ip, dropPort, sendloopDuration)
}
+// FilterInputDropOnlyUDP tests that "-p udp -j DROP" only affects UDP traffic.
+type FilterInputDropOnlyUDP struct{}
+
+// Name implements TestCase.Name.
+func (FilterInputDropOnlyUDP) Name() string {
+ return "FilterInputDropOnlyUDP"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterInputDropOnlyUDP) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "INPUT", "-p", "udp", "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for a TCP connection, which should be allowed.
+ if err := listenTCP(acceptPort, sendloopDuration); err != nil {
+ return fmt.Errorf("failed to establish a connection %v", err)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterInputDropOnlyUDP) LocalAction(ip net.IP) error {
+ // Try to establish a TCP connection with the container, which should
+ // succeed.
+ return connectTCP(ip, acceptPort, dropPort, sendloopDuration)
+}
+
// FilterInputDropUDPPort tests that we can drop UDP traffic by port.
type FilterInputDropUDPPort struct{}
@@ -122,3 +155,96 @@ func (FilterInputDropDifferentUDPPort) ContainerAction(ip net.IP) error {
func (FilterInputDropDifferentUDPPort) LocalAction(ip net.IP) error {
return sendUDPLoop(ip, acceptPort, sendloopDuration)
}
+
+// FilterInputDropTCPDestPort tests that connections are not accepted on specified source ports.
+type FilterInputDropTCPDestPort struct{}
+
+// Name implements TestCase.Name.
+func (FilterInputDropTCPDestPort) Name() string {
+ return "FilterInputDropTCPDestPort"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterInputDropTCPDestPort) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "INPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on drop port.
+ if err := listenTCP(dropPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterInputDropTCPDestPort) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, dropPort, acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// FilterInputDropTCPSrcPort tests that connections are not accepted on specified source ports.
+type FilterInputDropTCPSrcPort struct{}
+
+// Name implements TestCase.Name.
+func (FilterInputDropTCPSrcPort) Name() string {
+ return "FilterInputDropTCPSrcPort"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterInputDropTCPSrcPort) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "INPUT", "-p", "tcp", "-m", "tcp", "--sport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ if err := listenTCP(acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterInputDropTCPSrcPort) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, acceptPort, dropPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be acceptedi, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// FilterInputDropAll tests that we can drop all traffic to the INPUT chain.
+type FilterInputDropAll struct{}
+
+// Name implements TestCase.Name.
+func (FilterInputDropAll) Name() string {
+ return "FilterInputDropAll"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterInputDropAll) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "INPUT", "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for all packets on dropPort.
+ if err := listenUDP(dropPort, sendloopDuration); err == nil {
+ return fmt.Errorf("packets should have been dropped, but got a packet")
+ } else if netErr, ok := err.(net.Error); !ok || !netErr.Timeout() {
+ return fmt.Errorf("error reading: %v", err)
+ }
+
+ // At this point we know that reading timed out and never received a
+ // packet.
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterInputDropAll) LocalAction(ip net.IP) error {
+ return sendUDPLoop(ip, dropPort, sendloopDuration)
+}
diff --git a/test/iptables/filter_output.go b/test/iptables/filter_output.go
new file mode 100644
index 000000000..ee2c49f9a
--- /dev/null
+++ b/test/iptables/filter_output.go
@@ -0,0 +1,87 @@
+// 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.
+
+package iptables
+
+import (
+ "fmt"
+ "net"
+)
+
+func init() {
+ RegisterTestCase(FilterOutputDropTCPDestPort{})
+ RegisterTestCase(FilterOutputDropTCPSrcPort{})
+}
+
+// FilterOutputDropTCPDestPort tests that connections are not accepted on specified source ports.
+type FilterOutputDropTCPDestPort struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputDropTCPDestPort) Name() string {
+ return "FilterOutputDropTCPDestPort"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputDropTCPDestPort) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--dport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on accept port.
+ if err := listenTCP(acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputDropTCPDestPort) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, acceptPort, dropPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// FilterOutputDropTCPSrcPort tests that connections are not accepted on specified source ports.
+type FilterOutputDropTCPSrcPort struct{}
+
+// Name implements TestCase.Name.
+func (FilterOutputDropTCPSrcPort) Name() string {
+ return "FilterOutputDropTCPSrcPort"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (FilterOutputDropTCPSrcPort) ContainerAction(ip net.IP) error {
+ if err := filterTable("-A", "OUTPUT", "-p", "tcp", "-m", "tcp", "--sport", fmt.Sprintf("%d", dropPort), "-j", "DROP"); err != nil {
+ return err
+ }
+
+ // Listen for TCP packets on drop port.
+ if err := listenTCP(dropPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection on port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (FilterOutputDropTCPSrcPort) LocalAction(ip net.IP) error {
+ if err := connectTCP(ip, dropPort, acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("connection destined to port %d should not be accepted, but got accepted", dropPort)
+ }
+
+ return nil
+}
diff --git a/test/iptables/iptables_test.go b/test/iptables/iptables_test.go
index bfbf1bb87..41909582a 100644
--- a/test/iptables/iptables_test.go
+++ b/test/iptables/iptables_test.go
@@ -15,6 +15,7 @@
package iptables
import (
+ "flag"
"fmt"
"net"
"os"
@@ -22,15 +23,14 @@ import (
"testing"
"time"
- "flag"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/runsc/dockerutil"
"gvisor.dev/gvisor/runsc/testutil"
)
-const timeout time.Duration = 10 * time.Second
+const timeout = 18 * time.Second
-var image = flag.String("image", "bazel/test/iptables/runner:runner", "image to run tests in")
+var image = flag.String("image", "bazel/test/iptables/runner:runner-image", "image to run tests in")
type result struct {
output string
@@ -177,3 +177,51 @@ func TestFilterInputDropDifferentUDPPort(t *testing.T) {
t.Fatal(err)
}
}
+
+func TestFilterInputDropAll(t *testing.T) {
+ if err := singleTest(FilterInputDropAll{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFilterInputDropOnlyUDP(t *testing.T) {
+ if err := singleTest(FilterInputDropOnlyUDP{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestNATRedirectUDPPort(t *testing.T) {
+ if err := singleTest(NATRedirectUDPPort{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestNATDropUDP(t *testing.T) {
+ if err := singleTest(NATDropUDP{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFilterInputDropTCPDestPort(t *testing.T) {
+ if err := singleTest(FilterInputDropTCPDestPort{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFilterInputDropTCPSrcPort(t *testing.T) {
+ if err := singleTest(FilterInputDropTCPSrcPort{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFilterOutputDropTCPDestPort(t *testing.T) {
+ if err := singleTest(FilterOutputDropTCPDestPort{}); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestFilterOutputDropTCPSrcPort(t *testing.T) {
+ if err := singleTest(FilterOutputDropTCPSrcPort{}); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/test/iptables/iptables_util.go b/test/iptables/iptables_util.go
index 3a4d11f1a..043114c78 100644
--- a/test/iptables/iptables_util.go
+++ b/test/iptables/iptables_util.go
@@ -19,6 +19,8 @@ import (
"net"
"os/exec"
"time"
+
+ "gvisor.dev/gvisor/runsc/testutil"
)
const iptablesBinary = "iptables"
@@ -80,3 +82,51 @@ func sendUDPLoop(ip net.IP, port int, duration time.Duration) error {
return nil
}
+
+// listenTCP listens for connections on a TCP port.
+func listenTCP(port int, timeout time.Duration) error {
+ localAddr := net.TCPAddr{
+ Port: port,
+ }
+
+ // Starts listening on port.
+ lConn, err := net.ListenTCP("tcp4", &localAddr)
+ if err != nil {
+ return err
+ }
+ defer lConn.Close()
+
+ // Accept connections on port.
+ lConn.SetDeadline(time.Now().Add(timeout))
+ conn, err := lConn.AcceptTCP()
+ if err != nil {
+ return err
+ }
+ conn.Close()
+ return nil
+}
+
+// connectTCP connects the TCP server over specified local port, server IP and remote/server port.
+func connectTCP(ip net.IP, remotePort, localPort int, timeout time.Duration) error {
+ contAddr := net.TCPAddr{
+ IP: ip,
+ Port: remotePort,
+ }
+ // The container may not be listening when we first connect, so retry
+ // upon error.
+ callback := func() error {
+ localAddr := net.TCPAddr{
+ Port: localPort,
+ }
+ conn, err := net.DialTCP("tcp4", &localAddr, &contAddr)
+ if conn != nil {
+ conn.Close()
+ }
+ return err
+ }
+ if err := testutil.Poll(callback, timeout); err != nil {
+ return fmt.Errorf("timed out waiting to send IP, most recent error: %v", err)
+ }
+
+ return nil
+}
diff --git a/test/iptables/nat.go b/test/iptables/nat.go
new file mode 100644
index 000000000..b5c6f927e
--- /dev/null
+++ b/test/iptables/nat.go
@@ -0,0 +1,80 @@
+// 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.
+
+package iptables
+
+import (
+ "fmt"
+ "net"
+)
+
+const (
+ redirectPort = 42
+)
+
+func init() {
+ RegisterTestCase(NATRedirectUDPPort{})
+ RegisterTestCase(NATDropUDP{})
+}
+
+// NATRedirectUDPPort tests that packets are redirected to different port.
+type NATRedirectUDPPort struct{}
+
+// Name implements TestCase.Name.
+func (NATRedirectUDPPort) Name() string {
+ return "NATRedirectUDPPort"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATRedirectUDPPort) ContainerAction(ip net.IP) error {
+ if err := filterTable("-t", "nat", "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
+ return err
+ }
+
+ if err := listenUDP(redirectPort, sendloopDuration); err != nil {
+ return fmt.Errorf("packets on port %d should be allowed, but encountered an error: %v", redirectPort, err)
+ }
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATRedirectUDPPort) LocalAction(ip net.IP) error {
+ return sendUDPLoop(ip, acceptPort, sendloopDuration)
+}
+
+// NATDropUDP tests that packets are not received in ports other than redirect port.
+type NATDropUDP struct{}
+
+// Name implements TestCase.Name.
+func (NATDropUDP) Name() string {
+ return "NATDropUDP"
+}
+
+// ContainerAction implements TestCase.ContainerAction.
+func (NATDropUDP) ContainerAction(ip net.IP) error {
+ if err := filterTable("-t", "nat", "-A", "PREROUTING", "-p", "udp", "-j", "REDIRECT", "--to-ports", fmt.Sprintf("%d", redirectPort)); err != nil {
+ return err
+ }
+
+ if err := listenUDP(acceptPort, sendloopDuration); err == nil {
+ return fmt.Errorf("packets on port %d should have been redirected to port %d", acceptPort, redirectPort)
+ }
+
+ return nil
+}
+
+// LocalAction implements TestCase.LocalAction.
+func (NATDropUDP) LocalAction(ip net.IP) error {
+ return sendUDPLoop(ip, acceptPort, sendloopDuration)
+}
diff --git a/test/iptables/runner/BUILD b/test/iptables/runner/BUILD
index c6c42d870..b9199387a 100644
--- a/test/iptables/runner/BUILD
+++ b/test/iptables/runner/BUILD
@@ -1,15 +1,22 @@
-load("@io_bazel_rules_docker//go:image.bzl", "go_image")
-load("@io_bazel_rules_docker//container:container.bzl", "container_image")
+load("//tools:defs.bzl", "container_image", "go_binary", "go_image")
package(licenses = ["notice"])
+go_binary(
+ name = "runner",
+ testonly = 1,
+ srcs = ["main.go"],
+ deps = ["//test/iptables"],
+)
+
container_image(
name = "iptables-base",
base = "@iptables-test//image",
)
go_image(
- name = "runner",
+ name = "runner-image",
+ testonly = 1,
srcs = ["main.go"],
base = ":iptables-base",
deps = ["//test/iptables"],
diff --git a/test/root/BUILD b/test/root/BUILD
index d5dd9bca2..23ce2a70f 100644
--- a/test/root/BUILD
+++ b/test/root/BUILD
@@ -1,11 +1,10 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("//tools:defs.bzl", "go_library", "go_test")
package(licenses = ["notice"])
go_library(
name = "root",
srcs = ["root.go"],
- importpath = "gvisor.dev/gvisor/test/root",
)
go_test(
@@ -21,7 +20,7 @@ go_test(
data = [
"//runsc",
],
- embed = [":root"],
+ library = ":root",
tags = [
# Requires docker and runsc to be configured before the test runs.
# Also test only runs as root.
diff --git a/test/root/testdata/BUILD b/test/root/testdata/BUILD
index 125633680..bca5f9cab 100644
--- a/test/root/testdata/BUILD
+++ b/test/root/testdata/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//tools:defs.bzl", "go_library")
package(licenses = ["notice"])
@@ -12,7 +12,6 @@ go_library(
"sandbox.go",
"simple.go",
],
- importpath = "gvisor.dev/gvisor/test/root/testdata",
visibility = [
"//visibility:public",
],
diff --git a/test/runtimes/BUILD b/test/runtimes/BUILD
index 367295206..2c472bf8d 100644
--- a/test/runtimes/BUILD
+++ b/test/runtimes/BUILD
@@ -1,6 +1,6 @@
# These packages are used to run language runtime tests inside gVisor sandboxes.
-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test")
+load("//tools:defs.bzl", "go_binary", "go_test")
load("//test/runtimes:build_defs.bzl", "runtime_test")
package(licenses = ["notice"])
@@ -49,5 +49,5 @@ go_test(
name = "blacklist_test",
size = "small",
srcs = ["blacklist_test.go"],
- embed = [":runner"],
+ library = ":runner",
)
diff --git a/test/runtimes/build_defs.bzl b/test/runtimes/build_defs.bzl
index 6f84ca852..92e275a76 100644
--- a/test/runtimes/build_defs.bzl
+++ b/test/runtimes/build_defs.bzl
@@ -1,6 +1,6 @@
"""Defines a rule for runtime test targets."""
-load("@io_bazel_rules_go//go:def.bzl", "go_test")
+load("//tools:defs.bzl", "go_test", "loopback")
def runtime_test(
name,
@@ -34,6 +34,7 @@ def runtime_test(
]
data = [
":runner",
+ loopback,
]
if blacklist_file:
args += ["--blacklist_file", "test/runtimes/" + blacklist_file]
@@ -61,7 +62,7 @@ def blacklist_test(name, blacklist_file):
"""Test that a blacklist parses correctly."""
go_test(
name = name + "_blacklist_test",
- embed = [":runner"],
+ library = ":runner",
srcs = ["blacklist_test.go"],
args = ["--blacklist_file", "test/runtimes/" + blacklist_file],
data = [blacklist_file],
diff --git a/test/runtimes/images/proctor/BUILD b/test/runtimes/images/proctor/BUILD
index 09dc6c42f..85e004c45 100644
--- a/test/runtimes/images/proctor/BUILD
+++ b/test/runtimes/images/proctor/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test")
+load("//tools:defs.bzl", "go_binary", "go_test")
package(licenses = ["notice"])
@@ -19,7 +19,7 @@ go_test(
name = "proctor_test",
size = "small",
srcs = ["proctor_test.go"],
- embed = [":proctor"],
+ library = ":proctor",
deps = [
"//runsc/testutil",
],
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index a3a85917d..40e974314 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_binary")
+load("//tools:defs.bzl", "go_binary")
load("//test/syscalls:build_defs.bzl", "syscall_test")
package(licenses = ["notice"])
@@ -380,6 +380,8 @@ syscall_test(test = "//test/syscalls/linux:rseq_test")
syscall_test(test = "//test/syscalls/linux:rtsignal_test")
+syscall_test(test = "//test/syscalls/linux:signalfd_test")
+
syscall_test(test = "//test/syscalls/linux:sched_test")
syscall_test(test = "//test/syscalls/linux:sched_yield_test")
@@ -717,11 +719,6 @@ syscall_test(test = "//test/syscalls/linux:proc_net_tcp_test")
syscall_test(test = "//test/syscalls/linux:proc_net_udp_test")
-syscall_test(
- add_overlay = True,
- test = "//test/syscalls/linux:xattr_test",
-)
-
go_binary(
name = "syscall_test_runner",
testonly = 1,
diff --git a/test/syscalls/build_defs.bzl b/test/syscalls/build_defs.bzl
index aaf77c65b..1df761dd0 100644
--- a/test/syscalls/build_defs.bzl
+++ b/test/syscalls/build_defs.bzl
@@ -1,5 +1,7 @@
"""Defines a rule for syscall test targets."""
+load("//tools:defs.bzl", "loopback")
+
# syscall_test is a macro that will create targets to run the given test target
# on the host (native) and runsc.
def syscall_test(
@@ -135,6 +137,7 @@ def _syscall_test(
name = name,
data = [
":syscall_test_runner",
+ loopback,
test,
],
args = args,
@@ -148,6 +151,3 @@ def sh_test(**kwargs):
native.sh_test(
**kwargs
)
-
-def select_for_linux(for_linux, for_others = []):
- return for_linux
diff --git a/test/syscalls/gtest/BUILD b/test/syscalls/gtest/BUILD
index 9293f25cb..de4b2727c 100644
--- a/test/syscalls/gtest/BUILD
+++ b/test/syscalls/gtest/BUILD
@@ -1,12 +1,9 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//tools:defs.bzl", "go_library")
package(licenses = ["notice"])
go_library(
name = "gtest",
srcs = ["gtest.go"],
- importpath = "gvisor.dev/gvisor/test/syscalls/gtest",
- visibility = [
- "//test:__subpackages__",
- ],
+ visibility = ["//:sandbox"],
)
diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc
index a7cbee06b..2751fb4e7 100644
--- a/test/syscalls/linux/32bit.cc
+++ b/test/syscalls/linux/32bit.cc
@@ -15,10 +15,12 @@
#include <string.h>
#include <sys/mman.h>
+#include "gtest/gtest.h"
+#include "absl/base/macros.h"
#include "test/util/memory_util.h"
+#include "test/util/platform_util.h"
#include "test/util/posix_error.h"
#include "test/util/test_util.h"
-#include "gtest/gtest.h"
#ifndef __x86_64__
#error "This test is x86-64 specific."
@@ -30,7 +32,6 @@ namespace testing {
namespace {
constexpr char kInt3 = '\xcc';
-
constexpr char kInt80[2] = {'\xcd', '\x80'};
constexpr char kSyscall[2] = {'\x0f', '\x05'};
constexpr char kSysenter[2] = {'\x0f', '\x34'};
@@ -43,6 +44,7 @@ void ExitGroup32(const char instruction[2], int code) {
// Fill with INT 3 in case we execute too far.
memset(m.ptr(), kInt3, m.len());
+ // Copy in the actual instruction.
memcpy(m.ptr(), instruction, 2);
// We're playing *extremely* fast-and-loose with the various syscall ABIs
@@ -71,77 +73,94 @@ void ExitGroup32(const char instruction[2], int code) {
"iretl\n"
"int $3\n"
:
- : [code] "m"(code), [ip] "d"(m.ptr())
+ : [ code ] "m"(code), [ ip ] "d"(m.ptr())
: "rax", "rbx", "rsp");
}
constexpr int kExitCode = 42;
TEST(Syscall32Bit, Int80) {
- switch (GvisorPlatform()) {
- case Platform::kKVM:
- // TODO(b/111805002): 32-bit segments are broken (but not explictly
- // disabled).
- return;
- case Platform::kPtrace:
- // TODO(gvisor.dev/issue/167): The ptrace platform does not have a
- // consistent story here.
- return;
- case Platform::kNative:
+ switch (PlatformSupport32Bit()) {
+ case PlatformSupport::NotSupported:
+ break;
+ case PlatformSupport::Segfault:
+ EXPECT_EXIT(ExitGroup32(kInt80, kExitCode),
+ ::testing::KilledBySignal(SIGSEGV), "");
break;
- }
- // Upstream Linux. 32-bit syscalls allowed.
- EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42),
- "");
-}
+ case PlatformSupport::Ignored:
+ // Since the call is ignored, we'll hit the int3 trap.
+ EXPECT_EXIT(ExitGroup32(kInt80, kExitCode),
+ ::testing::KilledBySignal(SIGTRAP), "");
+ break;
-TEST(Syscall32Bit, Sysenter) {
- switch (GvisorPlatform()) {
- case Platform::kKVM:
- // TODO(b/111805002): See above.
- return;
- case Platform::kPtrace:
- // TODO(gvisor.dev/issue/167): See above.
- return;
- case Platform::kNative:
+ case PlatformSupport::Allowed:
+ EXPECT_EXIT(ExitGroup32(kInt80, kExitCode), ::testing::ExitedWithCode(42),
+ "");
break;
}
+}
- if (GetCPUVendor() == CPUVendor::kAMD) {
+TEST(Syscall32Bit, Sysenter) {
+ if (PlatformSupport32Bit() == PlatformSupport::Allowed &&
+ GetCPUVendor() == CPUVendor::kAMD) {
// SYSENTER is an illegal instruction in compatibility mode on AMD.
EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
::testing::KilledBySignal(SIGILL), "");
return;
}
- // Upstream Linux on !AMD, 32-bit syscalls allowed.
- EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode), ::testing::ExitedWithCode(42),
- "");
-}
+ switch (PlatformSupport32Bit()) {
+ case PlatformSupport::NotSupported:
+ break;
-TEST(Syscall32Bit, Syscall) {
- switch (GvisorPlatform()) {
- case Platform::kKVM:
- // TODO(b/111805002): See above.
- return;
- case Platform::kPtrace:
- // TODO(gvisor.dev/issue/167): See above.
- return;
- case Platform::kNative:
+ case PlatformSupport::Segfault:
+ EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
+ ::testing::KilledBySignal(SIGSEGV), "");
+ break;
+
+ case PlatformSupport::Ignored:
+ // See above, except expected code is SIGSEGV.
+ EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
+ ::testing::KilledBySignal(SIGSEGV), "");
+ break;
+
+ case PlatformSupport::Allowed:
+ EXPECT_EXIT(ExitGroup32(kSysenter, kExitCode),
+ ::testing::ExitedWithCode(42), "");
break;
}
+}
- if (GetCPUVendor() == CPUVendor::kIntel) {
+TEST(Syscall32Bit, Syscall) {
+ if (PlatformSupport32Bit() == PlatformSupport::Allowed &&
+ GetCPUVendor() == CPUVendor::kIntel) {
// SYSCALL is an illegal instruction in compatibility mode on Intel.
EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
::testing::KilledBySignal(SIGILL), "");
return;
}
- // Upstream Linux on !Intel, 32-bit syscalls allowed.
- EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode), ::testing::ExitedWithCode(42),
- "");
+ switch (PlatformSupport32Bit()) {
+ case PlatformSupport::NotSupported:
+ break;
+
+ case PlatformSupport::Segfault:
+ EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
+ ::testing::KilledBySignal(SIGSEGV), "");
+ break;
+
+ case PlatformSupport::Ignored:
+ // See above.
+ EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
+ ::testing::KilledBySignal(SIGILL), "");
+ break;
+
+ case PlatformSupport::Allowed:
+ EXPECT_EXIT(ExitGroup32(kSyscall, kExitCode),
+ ::testing::ExitedWithCode(42), "");
+ break;
+ }
}
// Far call code called below.
@@ -205,19 +224,20 @@ void FarCall32() {
}
TEST(Call32Bit, Disallowed) {
- switch (GvisorPlatform()) {
- case Platform::kKVM:
- // TODO(b/111805002): See above.
- return;
- case Platform::kPtrace:
- // The ptrace platform cannot prevent switching to compatibility mode.
- ABSL_FALLTHROUGH_INTENDED;
- case Platform::kNative:
+ switch (PlatformSupport32Bit()) {
+ case PlatformSupport::NotSupported:
break;
- }
- // Shouldn't crash.
- FarCall32();
+ case PlatformSupport::Segfault:
+ EXPECT_EXIT(FarCall32(), ::testing::KilledBySignal(SIGSEGV), "");
+ break;
+
+ case PlatformSupport::Ignored:
+ ABSL_FALLTHROUGH_INTENDED;
+ case PlatformSupport::Allowed:
+ // Shouldn't crash.
+ FarCall32();
+ }
}
} // namespace
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 68dcc598b..ee7a8a673 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -1,5 +1,4 @@
-load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library")
-load("//test/syscalls:build_defs.bzl", "select_for_linux")
+load("//tools:defs.bzl", "cc_binary", "cc_library", "default_net_util", "select_arch", "select_system")
package(
default_visibility = ["//:sandbox"],
@@ -19,16 +18,6 @@ exports_files(
visibility = ["//:sandbox"],
)
-config_setting(
- name = "x86_64",
- constraint_values = ["@bazel_tools//platforms:x86_64"],
-)
-
-config_setting(
- name = "aarch64",
- constraint_values = ["@bazel_tools//platforms:aarch64"],
-)
-
cc_binary(
name = "sigaltstack_check",
testonly = 1,
@@ -136,13 +125,11 @@ cc_library(
testonly = 1,
srcs = [
"socket_test_util.cc",
- ] + select_for_linux(
- [
- "socket_test_util_impl.cc",
- ],
- ),
+ "socket_test_util_impl.cc",
+ ],
hdrs = ["socket_test_util.h"],
- deps = [
+ defines = select_system(),
+ deps = default_net_util() + [
"@com_google_googletest//:gtest",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
@@ -153,8 +140,7 @@ cc_library(
"//test/util:temp_path",
"//test/util:test_util",
"//test/util:thread_util",
- ] + select_for_linux([
- ]),
+ ],
)
cc_library(
@@ -207,16 +193,18 @@ cc_binary(
cc_binary(
name = "32bit_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["32bit.cc"],
- ":aarch64": [],
- }),
+ srcs = select_arch(
+ amd64 = ["32bit.cc"],
+ arm64 = [],
+ ),
linkstatic = 1,
deps = [
"//test/util:memory_util",
+ "//test/util:platform_util",
"//test/util:posix_error",
"//test/util:test_main",
"//test/util:test_util",
+ "@com_google_absl//absl/base:core_headers",
"@com_google_googletest//:gtest",
],
)
@@ -496,6 +484,7 @@ cc_binary(
srcs = ["concurrency.cc"],
linkstatic = 1,
deps = [
+ "//test/util:platform_util",
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
@@ -597,13 +586,11 @@ cc_binary(
cc_binary(
name = "exceptions_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["exceptions.cc"],
- ":aarch64": [],
- }),
+ srcs = ["exceptions.cc"],
linkstatic = 1,
deps = [
"//test/util:logging",
+ "//test/util:platform_util",
"//test/util:signal_util",
"//test/util:test_main",
"//test/util:test_util",
@@ -656,10 +643,10 @@ cc_binary(
cc_binary(
name = "exec_binary_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["exec_binary.cc"],
- ":aarch64": [],
- }),
+ srcs = select_arch(
+ amd64 = ["exec_binary.cc"],
+ arm64 = [],
+ ),
linkstatic = 1,
deps = [
"//test/util:cleanup",
@@ -773,6 +760,7 @@ cc_binary(
":socket_test_util",
"//test/util:cleanup",
"//test/util:eventfd_util",
+ "//test/util:fs_util",
"//test/util:multiprocess_util",
"//test/util:posix_error",
"//test/util:save_util",
@@ -830,10 +818,10 @@ cc_binary(
cc_binary(
name = "fpsig_fork_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["fpsig_fork.cc"],
- ":aarch64": [],
- }),
+ srcs = select_arch(
+ amd64 = ["fpsig_fork.cc"],
+ arm64 = [],
+ ),
linkstatic = 1,
deps = [
"//test/util:logging",
@@ -847,10 +835,10 @@ cc_binary(
cc_binary(
name = "fpsig_nested_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["fpsig_nested.cc"],
- ":aarch64": [],
- }),
+ srcs = select_arch(
+ amd64 = ["fpsig_nested.cc"],
+ arm64 = [],
+ ),
linkstatic = 1,
deps = [
"//test/util:test_main",
@@ -1416,6 +1404,7 @@ cc_binary(
linkstatic = 1,
deps = [
"//test/util:file_descriptor",
+ "//test/util:fs_util",
"//test/util:posix_error",
"//test/util:temp_path",
"//test/util:test_main",
@@ -1465,12 +1454,10 @@ cc_binary(
cc_binary(
name = "arch_prctl_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["arch_prctl.cc"],
- ":aarch64": [],
- }),
+ srcs = ["arch_prctl.cc"],
linkstatic = 1,
deps = [
+ "//test/util:file_descriptor",
"//test/util:test_main",
"//test/util:test_util",
"@com_google_googletest//:gtest",
@@ -1689,6 +1676,7 @@ cc_binary(
deps = [
"//test/util:logging",
"//test/util:multiprocess_util",
+ "//test/util:platform_util",
"//test/util:signal_util",
"//test/util:test_util",
"//test/util:thread_util",
@@ -2063,21 +2051,21 @@ cc_binary(
cc_binary(
name = "sigiret_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["sigiret.cc"],
- ":aarch64": [],
- }),
+ srcs = select_arch(
+ amd64 = ["sigiret.cc"],
+ arm64 = [],
+ ),
linkstatic = 1,
deps = [
+ "@com_google_googletest//:gtest",
"//test/util:logging",
"//test/util:signal_util",
"//test/util:test_util",
"//test/util:timer_util",
- "@com_google_googletest//:gtest",
- ] + select({
- ":x86_64": [],
- ":aarch64": ["//test/util:test_main"],
- }),
+ ] + select_arch(
+ amd64 = [],
+ arm64 = ["//test/util:test_main"],
+ ),
)
cc_binary(
@@ -2205,6 +2193,7 @@ cc_library(
":socket_test_util",
"//test/util:test_util",
"//test/util:thread_util",
+ "@com_google_absl//absl/memory",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest",
],
@@ -2727,6 +2716,7 @@ cc_binary(
srcs = ["socket_inet_loopback.cc"],
linkstatic = 1,
deps = [
+ ":ip_socket_test_util",
":socket_test_util",
"//test/util:file_descriptor",
"//test/util:posix_error",
@@ -2922,7 +2912,6 @@ cc_library(
":unix_domain_socket_test_util",
"//test/util:test_util",
"//test/util:thread_util",
- "//test/util:timer_util",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest",
],
@@ -3294,10 +3283,7 @@ cc_binary(
cc_binary(
name = "sysret_test",
testonly = 1,
- srcs = select({
- ":x86_64": ["sysret.cc"],
- ":aarch64": [],
- }),
+ srcs = ["sysret.cc"],
linkstatic = 1,
deps = [
"//test/util:logging",
@@ -3420,11 +3406,11 @@ cc_library(
name = "udp_socket_test_cases",
testonly = 1,
srcs = [
- "udp_socket_test_cases.cc",
- ] + select_for_linux([
"udp_socket_errqueue_test_case.cc",
- ]),
+ "udp_socket_test_cases.cc",
+ ],
hdrs = ["udp_socket_test_cases.h"],
+ defines = select_system(),
deps = [
":socket_test_util",
":unix_domain_socket_test_util",
diff --git a/test/syscalls/linux/bad.cc b/test/syscalls/linux/bad.cc
index 9e4d8ea57..adfb149df 100644
--- a/test/syscalls/linux/bad.cc
+++ b/test/syscalls/linux/bad.cc
@@ -22,13 +22,18 @@ namespace gvisor {
namespace testing {
namespace {
-#if defined(__x86_64__)
+#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 = SYS_arch_specific_syscall + 15;
+#endif
+
TEST(BadSyscallTest, NotImplemented) {
- // get_kernel_syms is not supported in Linux > 2.6, and not implemented in
- // gVisor.
- EXPECT_THAT(syscall(SYS_get_kernel_syms), SyscallFailsWithErrno(ENOSYS));
+ EXPECT_THAT(syscall(kNotImplementedSyscall), SyscallFailsWithErrno(ENOSYS));
}
-#endif // defined(__x86_64__)
TEST(BadSyscallTest, NegativeOne) {
EXPECT_THAT(syscall(-1), SyscallFailsWithErrno(ENOSYS));
diff --git a/test/syscalls/linux/concurrency.cc b/test/syscalls/linux/concurrency.cc
index 00b96b34a..f41f99900 100644
--- a/test/syscalls/linux/concurrency.cc
+++ b/test/syscalls/linux/concurrency.cc
@@ -20,6 +20,7 @@
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
+#include "test/util/platform_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -99,6 +100,7 @@ TEST(ConcurrencyTest, MultiProcessMultithreaded) {
// Test that multiple processes can execute concurrently, even if one process
// never yields.
TEST(ConcurrencyTest, MultiProcessConcurrency) {
+ SKIP_IF(PlatformSupportMultiProcess() == PlatformSupport::NotSupported);
pid_t child_pid = fork();
if (child_pid == 0) {
diff --git a/test/syscalls/linux/connect_external.cc b/test/syscalls/linux/connect_external.cc
index bfe1da82e..1edb50e47 100644
--- a/test/syscalls/linux/connect_external.cc
+++ b/test/syscalls/linux/connect_external.cc
@@ -56,7 +56,7 @@ TEST_P(GoferStreamSeqpacketTest, Echo) {
ProtocolSocket proto;
std::tie(env, proto) = GetParam();
- char *val = getenv(env.c_str());
+ char* val = getenv(env.c_str());
ASSERT_NE(val, nullptr);
std::string root(val);
@@ -69,7 +69,7 @@ TEST_P(GoferStreamSeqpacketTest, Echo) {
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, socket_path.c_str(), socket_path.length());
- ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr *>(&addr),
+ ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr)),
SyscallSucceeds());
@@ -92,7 +92,7 @@ TEST_P(GoferStreamSeqpacketTest, NonListening) {
ProtocolSocket proto;
std::tie(env, proto) = GetParam();
- char *val = getenv(env.c_str());
+ char* val = getenv(env.c_str());
ASSERT_NE(val, nullptr);
std::string root(val);
@@ -105,7 +105,7 @@ TEST_P(GoferStreamSeqpacketTest, NonListening) {
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, socket_path.c_str(), socket_path.length());
- ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr *>(&addr),
+ ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr)),
SyscallFailsWithErrno(ECONNREFUSED));
}
@@ -127,7 +127,7 @@ using GoferDgramTest = ::testing::TestWithParam<std::string>;
// unnamed. The server thus has no way to reply to us.
TEST_P(GoferDgramTest, Null) {
std::string env = GetParam();
- char *val = getenv(env.c_str());
+ char* val = getenv(env.c_str());
ASSERT_NE(val, nullptr);
std::string root(val);
@@ -140,7 +140,7 @@ TEST_P(GoferDgramTest, Null) {
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, socket_path.c_str(), socket_path.length());
- ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr *>(&addr),
+ ASSERT_THAT(connect(sock.get(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr)),
SyscallSucceeds());
diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc
index 367682c3d..927001eee 100644
--- a/test/syscalls/linux/eventfd.cc
+++ b/test/syscalls/linux/eventfd.cc
@@ -132,6 +132,31 @@ TEST(EventfdTest, BigWriteBigRead) {
EXPECT_EQ(l[0], 1);
}
+TEST(EventfdTest, SpliceFromPipePartialSucceeds) {
+ int pipes[2];
+ ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds());
+ const FileDescriptor pipe_rfd(pipes[0]);
+ const FileDescriptor pipe_wfd(pipes[1]);
+ constexpr uint64_t kVal{1};
+
+ FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK));
+
+ uint64_t event_array[2];
+ event_array[0] = kVal;
+ event_array[1] = kVal;
+ ASSERT_THAT(write(pipe_wfd.get(), event_array, sizeof(event_array)),
+ SyscallSucceedsWithValue(sizeof(event_array)));
+ EXPECT_THAT(splice(pipe_rfd.get(), /*__offin=*/nullptr, efd.get(),
+ /*__offout=*/nullptr, sizeof(event_array[0]) + 1,
+ SPLICE_F_NONBLOCK),
+ SyscallSucceedsWithValue(sizeof(event_array[0])));
+
+ uint64_t val;
+ ASSERT_THAT(read(efd.get(), &val, sizeof(val)),
+ SyscallSucceedsWithValue(sizeof(val)));
+ EXPECT_EQ(val, kVal);
+}
+
// NotifyNonZero is inherently racy, so random save is disabled.
TEST(EventfdTest, NotifyNonZero_NoRandomSave) {
// Waits will time out at 10 seconds.
diff --git a/test/syscalls/linux/exceptions.cc b/test/syscalls/linux/exceptions.cc
index 3d564e720..420b9543f 100644
--- a/test/syscalls/linux/exceptions.cc
+++ b/test/syscalls/linux/exceptions.cc
@@ -16,6 +16,7 @@
#include "gtest/gtest.h"
#include "test/util/logging.h"
+#include "test/util/platform_util.h"
#include "test/util/signal_util.h"
#include "test/util/test_util.h"
@@ -324,6 +325,7 @@ TEST(ExceptionTest, AlignmentHalt) {
}
TEST(ExceptionTest, AlignmentCheck) {
+ SKIP_IF(PlatformSupportAlignmentCheck() != PlatformSupport::Allowed);
// See above.
struct sigaction sa = {};
diff --git a/test/syscalls/linux/fault.cc b/test/syscalls/linux/fault.cc
index f6e19026f..a85750382 100644
--- a/test/syscalls/linux/fault.cc
+++ b/test/syscalls/linux/fault.cc
@@ -37,6 +37,9 @@ int GetPcFromUcontext(ucontext_t* uc, uintptr_t* pc) {
#elif defined(__i386__)
*pc = uc->uc_mcontext.gregs[REG_EIP];
return 1;
+#elif defined(__aarch64__)
+ *pc = uc->uc_mcontext.pc;
+ return 1;
#else
return 0;
#endif
diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc
index 4f3aa81d6..421c15b87 100644
--- a/test/syscalls/linux/fcntl.cc
+++ b/test/syscalls/linux/fcntl.cc
@@ -31,6 +31,7 @@
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/cleanup.h"
#include "test/util/eventfd_util.h"
+#include "test/util/fs_util.h"
#include "test/util/multiprocess_util.h"
#include "test/util/posix_error.h"
#include "test/util/save_util.h"
@@ -55,10 +56,6 @@ ABSL_FLAG(int32_t, socket_fd, -1,
namespace gvisor {
namespace testing {
-// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0
-// because "it isn't needed", even though Linux can return it via F_GETFL.
-constexpr int kOLargeFile = 00100000;
-
class FcntlLockTest : public ::testing::Test {
public:
void SetUp() override {
diff --git a/test/syscalls/linux/fpsig_fork.cc b/test/syscalls/linux/fpsig_fork.cc
index e7e9f06a1..a346f1f00 100644
--- a/test/syscalls/linux/fpsig_fork.cc
+++ b/test/syscalls/linux/fpsig_fork.cc
@@ -76,8 +76,8 @@ TEST(FPSigTest, Fork) {
"movl %[sig], %%edx;"
"syscall;"
:
- : [killnr] "i"(__NR_tgkill), [parent] "rm"(parent),
- [tid] "rm"(parent_tid), [sig] "i"(SIGUSR1)
+ : [ killnr ] "i"(__NR_tgkill), [ parent ] "rm"(parent),
+ [ tid ] "rm"(parent_tid), [ sig ] "i"(SIGUSR1)
: "rax", "rdi", "rsi", "rdx",
// Clobbered by syscall.
"rcx", "r11");
diff --git a/test/syscalls/linux/fpsig_nested.cc b/test/syscalls/linux/fpsig_nested.cc
index 395463aed..c476a8e7a 100644
--- a/test/syscalls/linux/fpsig_nested.cc
+++ b/test/syscalls/linux/fpsig_nested.cc
@@ -61,8 +61,8 @@ void sigusr1(int s, siginfo_t* siginfo, void* _uc) {
"movl %[sig], %%edx;"
"syscall;"
:
- : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid),
- [sig] "i"(SIGUSR2)
+ : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid),
+ [ sig ] "i"(SIGUSR2)
: "rax", "rdi", "rsi", "rdx",
// Clobbered by syscall.
"rcx", "r11");
@@ -107,8 +107,8 @@ TEST(FPSigTest, NestedSignals) {
"movl %[sig], %%edx;"
"syscall;"
:
- : [killnr] "i"(__NR_tgkill), [pid] "rm"(pid), [tid] "rm"(tid),
- [sig] "i"(SIGUSR1)
+ : [ killnr ] "i"(__NR_tgkill), [ pid ] "rm"(pid), [ tid ] "rm"(tid),
+ [ sig ] "i"(SIGUSR1)
: "rax", "rdi", "rsi", "rdx",
// Clobbered by syscall.
"rcx", "r11");
diff --git a/test/syscalls/linux/getdents.cc b/test/syscalls/linux/getdents.cc
index bfd18d4ff..b147d6181 100644
--- a/test/syscalls/linux/getdents.cc
+++ b/test/syscalls/linux/getdents.cc
@@ -228,6 +228,8 @@ class GetdentsTest : public ::testing::Test {
// 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() {
@@ -246,8 +248,7 @@ typedef ::testing::Types<struct linux_dirent, struct linux_dirent64>
GetdentsTypes;
#elif __aarch64__
// Test only getdents64 on arm64.
-typedef ::testing::Types<struct linux_dirent64>
- GetdentsTypes;
+typedef ::testing::Types<struct linux_dirent64> GetdentsTypes;
#endif
TYPED_TEST_SUITE(GetdentsTest, GetdentsTypes);
diff --git a/test/syscalls/linux/getrusage.cc b/test/syscalls/linux/getrusage.cc
index 9bdb1e4cd..0e51d42a8 100644
--- a/test/syscalls/linux/getrusage.cc
+++ b/test/syscalls/linux/getrusage.cc
@@ -67,7 +67,7 @@ TEST(GetrusageTest, Grandchild) {
pid = fork();
if (pid == 0) {
int flags = MAP_ANONYMOUS | MAP_POPULATE | MAP_PRIVATE;
- void *addr =
+ void* addr =
mmap(nullptr, kGrandchildSizeKb * 1024, PROT_WRITE, flags, -1, 0);
TEST_PCHECK(addr != MAP_FAILED);
} else {
diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc
index 7384c27dc..fdef646eb 100644
--- a/test/syscalls/linux/inotify.cc
+++ b/test/syscalls/linux/inotify.cc
@@ -977,7 +977,7 @@ TEST(Inotify, WatchOnRelativePath) {
ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY));
// Change working directory to root.
- const char* old_working_dir = get_current_dir_name();
+ 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.
@@ -997,7 +997,7 @@ TEST(Inotify, WatchOnRelativePath) {
// 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(chdir(old_working_dir), SyscallSucceeds());
+ EXPECT_THAT(fchdir(cwd.get()), SyscallSucceeds());
}
TEST(Inotify, ZeroLengthReadWriteDoesNotGenerateEvent) {
@@ -1591,6 +1591,34 @@ TEST(Inotify, EpollNoDeadlock) {
}
}
+TEST(Inotify, SpliceEvent) {
+ int pipes[2];
+ ASSERT_THAT(pipe2(pipes, 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, pipes[1], nullptr,
+ sizeof(struct inotify_event) + 1, SPLICE_F_NONBLOCK),
+ SyscallSucceedsWithValue(sizeof(struct inotify_event)));
+
+ const FileDescriptor read_fd(pipes[0]);
+ const std::vector<Event> events =
+ ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(read_fd.get()));
+ ASSERT_THAT(events, Are({Event(IN_ACCESS, watcher)}));
+}
+
} // 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
index 8398fc95f..6b472eb2f 100644
--- a/test/syscalls/linux/ip_socket_test_util.cc
+++ b/test/syscalls/linux/ip_socket_test_util.cc
@@ -187,24 +187,24 @@ PosixErrorOr<int> IfAddrHelper::GetIndex(std::string name) {
return InterfaceIndex(name);
}
-std::string GetAddr4Str(in_addr* a) {
+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(in6_addr* a) {
+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(sockaddr* a) {
+std::string GetAddrStr(const sockaddr* a) {
if (a->sa_family == AF_INET) {
- auto src = &(reinterpret_cast<sockaddr_in*>(a)->sin_addr);
+ auto src = &(reinterpret_cast<const sockaddr_in*>(a)->sin_addr);
return GetAddr4Str(src);
} else if (a->sa_family == AF_INET6) {
- auto src = &(reinterpret_cast<sockaddr_in6*>(a)->sin6_addr);
+ auto src = &(reinterpret_cast<const sockaddr_in6*>(a)->sin6_addr);
return GetAddr6Str(src);
}
return std::string("<invalid>");
diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h
index 9cb4566db..0f58e0f77 100644
--- a/test/syscalls/linux/ip_socket_test_util.h
+++ b/test/syscalls/linux/ip_socket_test_util.h
@@ -105,14 +105,14 @@ class IfAddrHelper {
};
// GetAddr4Str returns the given IPv4 network address structure as a string.
-std::string GetAddr4Str(in_addr* a);
+std::string GetAddr4Str(const in_addr* a);
// GetAddr6Str returns the given IPv6 network address structure as a string.
-std::string GetAddr6Str(in6_addr* a);
+std::string GetAddr6Str(const in6_addr* a);
// GetAddrStr returns the given IPv4 or IPv6 network address structure as a
// string.
-std::string GetAddrStr(sockaddr* a);
+std::string GetAddrStr(const sockaddr* a);
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/iptables.h b/test/syscalls/linux/iptables.h
index 616bea550..0719c60a4 100644
--- a/test/syscalls/linux/iptables.h
+++ b/test/syscalls/linux/iptables.h
@@ -188,7 +188,7 @@ struct ipt_replace {
unsigned int num_counters;
// The unchanged values from each ipt_entry's counters.
- struct xt_counters *counters;
+ struct xt_counters* counters;
// The entries to write to the table. This will run past the size defined by
// sizeof(srtuct ipt_replace);
diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc
index dd5352954..e74fa2ed5 100644
--- a/test/syscalls/linux/link.cc
+++ b/test/syscalls/linux/link.cc
@@ -55,7 +55,8 @@ TEST(LinkTest, CanCreateLinkFile) {
const std::string newname = NewTempAbsPath();
// Get the initial link count.
- uint64_t initial_link_count = ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path()));
+ uint64_t initial_link_count =
+ ASSERT_NO_ERRNO_AND_VALUE(Links(oldfile.path()));
EXPECT_THAT(link(oldfile.path().c_str(), newname.c_str()), SyscallSucceeds());
diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc
index 7fd0ea20c..5a1973f60 100644
--- a/test/syscalls/linux/madvise.cc
+++ b/test/syscalls/linux/madvise.cc
@@ -139,7 +139,7 @@ TEST(MadviseDontneedTest, IgnoresPermissions) {
TEST(MadviseDontforkTest, AddressLength) {
auto m =
ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE));
- char *addr = static_cast<char *>(m.ptr());
+ char* addr = static_cast<char*>(m.ptr());
// Address must be page aligned.
EXPECT_THAT(madvise(addr + 1, kPageSize, MADV_DONTFORK),
@@ -168,9 +168,9 @@ TEST(MadviseDontforkTest, DontforkShared) {
Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
nullptr, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0));
- const Mapping ms1 = Mapping(reinterpret_cast<void *>(m.addr()), kPageSize);
+ const Mapping ms1 = Mapping(reinterpret_cast<void*>(m.addr()), kPageSize);
const Mapping ms2 =
- Mapping(reinterpret_cast<void *>(m.addr() + kPageSize), kPageSize);
+ Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize);
m.release();
ASSERT_THAT(madvise(ms2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds());
@@ -197,11 +197,11 @@ TEST(MadviseDontforkTest, DontforkAnonPrivate) {
// Mmap three anonymous pages and MADV_DONTFORK the middle page.
Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
MmapAnon(kPageSize * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE));
- const Mapping mp1 = Mapping(reinterpret_cast<void *>(m.addr()), kPageSize);
+ const Mapping mp1 = Mapping(reinterpret_cast<void*>(m.addr()), kPageSize);
const Mapping mp2 =
- Mapping(reinterpret_cast<void *>(m.addr() + kPageSize), kPageSize);
+ Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize);
const Mapping mp3 =
- Mapping(reinterpret_cast<void *>(m.addr() + 2 * kPageSize), kPageSize);
+ Mapping(reinterpret_cast<void*>(m.addr() + 2 * kPageSize), kPageSize);
m.release();
ASSERT_THAT(madvise(mp2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds());
diff --git a/test/syscalls/linux/mempolicy.cc b/test/syscalls/linux/mempolicy.cc
index 9d5f47651..059fad598 100644
--- a/test/syscalls/linux/mempolicy.cc
+++ b/test/syscalls/linux/mempolicy.cc
@@ -43,17 +43,17 @@ namespace {
#define MPOL_MF_MOVE (1 << 1)
#define MPOL_MF_MOVE_ALL (1 << 2)
-int get_mempolicy(int *policy, uint64_t *nmask, uint64_t maxnode, void *addr,
+int get_mempolicy(int* policy, uint64_t* nmask, uint64_t maxnode, void* addr,
int flags) {
return syscall(SYS_get_mempolicy, policy, nmask, maxnode, addr, flags);
}
-int set_mempolicy(int mode, uint64_t *nmask, uint64_t maxnode) {
+int set_mempolicy(int mode, uint64_t* nmask, uint64_t maxnode) {
return syscall(SYS_set_mempolicy, mode, nmask, maxnode);
}
-int mbind(void *addr, unsigned long len, int mode,
- const unsigned long *nodemask, unsigned long maxnode,
+int mbind(void* addr, unsigned long len, int mode,
+ const unsigned long* nodemask, unsigned long maxnode,
unsigned flags) {
return syscall(SYS_mbind, addr, len, mode, nodemask, maxnode, flags);
}
@@ -68,7 +68,7 @@ Cleanup ScopedMempolicy() {
// Temporarily change the memory policy for the calling thread within the
// caller's scope.
-PosixErrorOr<Cleanup> ScopedSetMempolicy(int mode, uint64_t *nmask,
+PosixErrorOr<Cleanup> ScopedSetMempolicy(int mode, uint64_t* nmask,
uint64_t maxnode) {
if (set_mempolicy(mode, nmask, maxnode)) {
return PosixError(errno, "set_mempolicy");
diff --git a/test/syscalls/linux/mlock.cc b/test/syscalls/linux/mlock.cc
index 620b4f8b4..367a90fe1 100644
--- a/test/syscalls/linux/mlock.cc
+++ b/test/syscalls/linux/mlock.cc
@@ -60,7 +60,6 @@ bool IsPageMlocked(uintptr_t addr) {
return true;
}
-
TEST(MlockTest, Basic) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(CanMlock()));
auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(
diff --git a/test/syscalls/linux/msync.cc b/test/syscalls/linux/msync.cc
index ac7146017..2b2b6aef9 100644
--- a/test/syscalls/linux/msync.cc
+++ b/test/syscalls/linux/msync.cc
@@ -60,9 +60,7 @@ std::vector<std::function<PosixErrorOr<Mapping>()>> SyncableMappings() {
for (int const mflags : {MAP_PRIVATE, MAP_SHARED}) {
int const prot = PROT_READ | (writable ? PROT_WRITE : 0);
int const oflags = O_CREAT | (writable ? O_RDWR : O_RDONLY);
- funcs.push_back([=] {
- return MmapAnon(kPageSize, prot, mflags);
- });
+ funcs.push_back([=] { return MmapAnon(kPageSize, prot, mflags); });
funcs.push_back([=]() -> PosixErrorOr<Mapping> {
std::string const path = NewTempAbsPath();
ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, oflags, 0644));
diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc
index 33822ee57..df7129acc 100644
--- a/test/syscalls/linux/partial_bad_buffer.cc
+++ b/test/syscalls/linux/partial_bad_buffer.cc
@@ -18,7 +18,9 @@
#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>
@@ -62,9 +64,9 @@ class PartialBadBufferTest : public ::testing::Test {
// 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);
@@ -79,6 +81,15 @@ class PartialBadBufferTest : public ::testing::Test {
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());
@@ -165,97 +176,99 @@ TEST_F(PartialBadBufferTest, PreadvSmall) {
}
TEST_F(PartialBadBufferTest, WriteBig) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(write)(fd_, bad_buffer_, kPageSize),
- SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(write)(fd_, bad_buffer_, 10),
- SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(pwrite)(fd_, bad_buffer_, kPageSize, 0),
- SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(pwrite)(fd_, bad_buffer_, 10, 0),
- SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
-
struct iovec vec;
vec.iov_base = bad_buffer_;
vec.iov_len = kPageSize;
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(writev)(fd_, &vec, 1), SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
-
struct iovec vec;
vec.iov_base = bad_buffer_;
vec.iov_len = 10;
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(writev)(fd_, &vec, 1), SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
-
struct iovec vec;
vec.iov_base = bad_buffer_;
vec.iov_len = kPageSize;
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(pwritev)(fd_, &vec, 1, 0),
- SyscallFailsWithErrno(EFAULT));
+ 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) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
-
struct iovec vec;
vec.iov_base = bad_buffer_;
vec.iov_len = 10;
+ off_t orig_size = Size();
+ int n;
- EXPECT_THAT(RetryEINTR(pwritev)(fd_, &vec, 1, 0),
- SyscallFailsWithErrno(EFAULT));
+ 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
@@ -283,29 +296,6 @@ TEST_F(PartialBadBufferTest, GetdentsOneEntry) {
SyscallSucceedsWithValue(Gt(0)));
}
-// Verify that when write returns EFAULT the kernel hasn't silently written
-// the initial valid bytes.
-TEST_F(PartialBadBufferTest, WriteEfaultIsntPartial) {
- // FIXME(b/24788078): The sentry write syscalls will return immediately
- // if Access returns an error, but Access may not return an error
- // and the sentry will instead perform a partial write.
- SKIP_IF(IsRunningOnGvisor());
-
- bad_buffer_[0] = 'A';
- EXPECT_THAT(RetryEINTR(write)(fd_, bad_buffer_, 10),
- SyscallFailsWithErrno(EFAULT));
-
- size_t size = 255;
- char buf[255];
- memset(buf, 0, size);
-
- EXPECT_THAT(RetryEINTR(pread)(fd_, buf, size, 0),
- SyscallSucceedsWithValue(sizeof(kMessage) - 1));
-
- // 'A' has not been written.
- EXPECT_STREQ(buf, kMessage);
-}
-
PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) {
struct sockaddr_storage addr;
memset(&addr, 0, sizeof(addr));
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index ac9b21b24..d8e19e910 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -25,6 +25,7 @@
#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"
@@ -144,11 +145,10 @@ TEST_P(PipeTest, Flags) {
if (IsNamedPipe()) {
// May be stubbed to zero; define locally.
- constexpr int kLargefile = 0100000;
EXPECT_THAT(fcntl(rfd_.get(), F_GETFL),
- SyscallSucceedsWithValue(kLargefile | O_RDONLY));
+ SyscallSucceedsWithValue(kOLargeFile | O_RDONLY));
EXPECT_THAT(fcntl(wfd_.get(), F_GETFL),
- SyscallSucceedsWithValue(kLargefile | O_WRONLY));
+ 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));
diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc
index 9e5aa7fd0..c42472474 100644
--- a/test/syscalls/linux/poll.cc
+++ b/test/syscalls/linux/poll.cc
@@ -275,7 +275,8 @@ TEST_F(PollTest, Nfds) {
// 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, {.fd = efd.get(), .events = POLLOUT});
+ 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));
diff --git a/test/syscalls/linux/preadv2.cc b/test/syscalls/linux/preadv2.cc
index 3eeaf6ad8..4a9acd7ae 100644
--- a/test/syscalls/linux/preadv2.cc
+++ b/test/syscalls/linux/preadv2.cc
@@ -204,7 +204,7 @@ TEST(Preadv2Test, TestInvalidOffset) {
iov[0].iov_len = 0;
EXPECT_THAT(preadv2(fd.get(), iov.get(), /*iovcnt=*/1, /*offset=*/-8,
- /*flags=*/RWF_HIPRI),
+ /*flags=*/0),
SyscallFailsWithErrno(EINVAL));
}
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index 5b4f29cd9..169b723eb 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -100,9 +100,39 @@ namespace {
#define SUID_DUMP_ROOT 2
#endif /* SUID_DUMP_ROOT */
-// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0
-// because "it isn't needed", even though Linux can return it via F_GETFL.
-constexpr int kOLargeFile = 00100000;
+#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.
@@ -717,28 +747,6 @@ TEST(ProcCpuinfo, RequiredFieldsArePresent) {
ASSERT_FALSE(proc_cpuinfo.empty());
std::vector<std::string> cpuinfo_fields = absl::StrSplit(proc_cpuinfo, '\n');
- // 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",
- };
-
// Check that the usual fields are there. We don't really care about the
// contents.
for (const std::string& field : required_fields) {
diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc
index 65bad06d4..3a611a86f 100644
--- a/test/syscalls/linux/proc_net.cc
+++ b/test/syscalls/linux/proc_net.cc
@@ -68,8 +68,8 @@ TEST(ProcSysNetIpv4Sack, CanReadAndWrite) {
}
PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp,
- const std::string &type,
- const std::string &item) {
+ const std::string& type,
+ const std::string& item) {
std::vector<std::string> snmp_vec = absl::StrSplit(snmp, '\n');
// /proc/net/snmp prints a line of headers followed by a line of metrics.
@@ -127,7 +127,7 @@ TEST(ProcNetSnmp, TcpReset_NoRandomSave) {
};
ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
- ASSERT_THAT(connect(s.get(), (struct sockaddr *)&sin, sizeof(sin)),
+ ASSERT_THAT(connect(s.get(), (struct sockaddr*)&sin, sizeof(sin)),
SyscallFailsWithErrno(ECONNREFUSED));
uint64_t newAttemptFails;
@@ -172,19 +172,19 @@ TEST(ProcNetSnmp, TcpEstab_NoRandomSave) {
};
ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
- ASSERT_THAT(bind(s_listen.get(), (struct sockaddr *)&sin, sizeof(sin)),
+ ASSERT_THAT(bind(s_listen.get(), (struct sockaddr*)&sin, sizeof(sin)),
SyscallSucceeds());
ASSERT_THAT(listen(s_listen.get(), 1), SyscallSucceeds());
// Get the port bound by the listening socket.
socklen_t addrlen = sizeof(sin);
ASSERT_THAT(
- getsockname(s_listen.get(), reinterpret_cast<sockaddr *>(&sin), &addrlen),
+ getsockname(s_listen.get(), reinterpret_cast<sockaddr*>(&sin), &addrlen),
SyscallSucceeds());
FileDescriptor s_connect =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, 0));
- ASSERT_THAT(connect(s_connect.get(), (struct sockaddr *)&sin, sizeof(sin)),
+ ASSERT_THAT(connect(s_connect.get(), (struct sockaddr*)&sin, sizeof(sin)),
SyscallSucceeds());
auto s_accept =
@@ -260,7 +260,7 @@ TEST(ProcNetSnmp, UdpNoPorts_NoRandomSave) {
.sin_port = htons(4444),
};
ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
- ASSERT_THAT(sendto(s.get(), "a", 1, 0, (struct sockaddr *)&sin, sizeof(sin)),
+ ASSERT_THAT(sendto(s.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)),
SyscallSucceedsWithValue(1));
uint64_t newOutDatagrams;
@@ -295,18 +295,18 @@ TEST(ProcNetSnmp, UdpIn) {
.sin_port = htons(0),
};
ASSERT_EQ(inet_pton(AF_INET, "127.0.0.1", &(sin.sin_addr)), 1);
- ASSERT_THAT(bind(server.get(), (struct sockaddr *)&sin, sizeof(sin)),
+ ASSERT_THAT(bind(server.get(), (struct sockaddr*)&sin, sizeof(sin)),
SyscallSucceeds());
// Get the port bound by the server socket.
socklen_t addrlen = sizeof(sin);
ASSERT_THAT(
- getsockname(server.get(), reinterpret_cast<sockaddr *>(&sin), &addrlen),
+ getsockname(server.get(), reinterpret_cast<sockaddr*>(&sin), &addrlen),
SyscallSucceeds());
FileDescriptor client =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
ASSERT_THAT(
- sendto(client.get(), "a", 1, 0, (struct sockaddr *)&sin, sizeof(sin)),
+ sendto(client.get(), "a", 1, 0, (struct sockaddr*)&sin, sizeof(sin)),
SyscallSucceedsWithValue(1));
char buf[128];
diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc
index 8f3800380..4dd5cf27b 100644
--- a/test/syscalls/linux/ptrace.cc
+++ b/test/syscalls/linux/ptrace.cc
@@ -32,6 +32,7 @@
#include "absl/time/time.h"
#include "test/util/logging.h"
#include "test/util/multiprocess_util.h"
+#include "test/util/platform_util.h"
#include "test/util/signal_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -178,7 +179,8 @@ TEST(PtraceTest, GetSigMask) {
// Install a signal handler for kBlockSignal to avoid termination and block
// it.
- TEST_PCHECK(signal(kBlockSignal, +[](int signo) {}) != SIG_ERR);
+ TEST_PCHECK(signal(
+ kBlockSignal, +[](int signo) {}) != SIG_ERR);
MaybeSave();
TEST_PCHECK(sigprocmask(SIG_SETMASK, &blocked, nullptr) == 0);
MaybeSave();
@@ -823,13 +825,8 @@ TEST(PtraceTest,
// These tests requires knowledge of architecture-specific syscall convention.
#ifdef __x86_64__
TEST(PtraceTest, Int3) {
- switch (GvisorPlatform()) {
- case Platform::kKVM:
- // TODO(b/124248694): int3 isn't handled properly.
- return;
- default:
- break;
- }
+ SKIP_IF(PlatformSupportInt3() == PlatformSupport::NotSupported);
+
pid_t const child_pid = fork();
if (child_pid == 0) {
// In child process.
diff --git a/test/syscalls/linux/readv_common.cc b/test/syscalls/linux/readv_common.cc
index 491d5f40f..2694dc64f 100644
--- a/test/syscalls/linux/readv_common.cc
+++ b/test/syscalls/linux/readv_common.cc
@@ -154,7 +154,7 @@ void ReadBuffersOverlapping(int fd) {
char* expected_ptr = expected.data();
memcpy(expected_ptr, &kReadvTestData[overlap_bytes], overlap_bytes);
memcpy(&expected_ptr[overlap_bytes], &kReadvTestData[overlap_bytes],
- kReadvTestDataSize);
+ kReadvTestDataSize - overlap_bytes);
struct iovec iovs[2];
iovs[0].iov_base = buffer.data();
diff --git a/test/syscalls/linux/rseq/BUILD b/test/syscalls/linux/rseq/BUILD
index 5cfe4e56f..ed488dbc2 100644
--- a/test/syscalls/linux/rseq/BUILD
+++ b/test/syscalls/linux/rseq/BUILD
@@ -1,8 +1,7 @@
# This package contains a standalone rseq test binary. This binary must not
# depend on libc, which might use rseq itself.
-load("@bazel_tools//tools/cpp:cc_flags_supplier.bzl", "cc_flags_supplier")
-load("@rules_cc//cc:defs.bzl", "cc_library")
+load("//tools:defs.bzl", "cc_flags_supplier", "cc_library", "cc_toolchain")
package(licenses = ["notice"])
@@ -37,8 +36,8 @@ genrule(
"$(location start.S)",
]),
toolchains = [
+ cc_toolchain,
":no_pie_cc_flags",
- "@bazel_tools//tools/cpp:current_cc_toolchain",
],
visibility = ["//:sandbox"],
)
diff --git a/test/syscalls/linux/seccomp.cc b/test/syscalls/linux/seccomp.cc
index 6d7e543b9..2c947feb7 100644
--- a/test/syscalls/linux/seccomp.cc
+++ b/test/syscalls/linux/seccomp.cc
@@ -52,8 +52,8 @@ namespace {
#ifdef __x86_64__
constexpr uint32_t kFilteredSyscall = SYS_vserver;
#elif __aarch64__
-// Using arch_specific_syscalls which are not implemented on arm64.
-constexpr uint32_t kFilteredSyscall = SYS_arch_specific_syscall+15;
+// Use the last of arch_specific_syscalls which are not implemented on arm64.
+constexpr uint32_t kFilteredSyscall = SYS_arch_specific_syscall + 15;
#endif
// Applies a seccomp-bpf filter that returns `filtered_result` for
@@ -118,7 +118,8 @@ TEST(SeccompTest, RetKillCausesDeathBySIGSYS) {
pid_t const pid = fork();
if (pid == 0) {
// Register a signal handler for SIGSYS that we don't expect to be invoked.
- RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); });
+ RegisterSignalHandler(
+ SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); });
ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL);
syscall(kFilteredSyscall);
TEST_CHECK_MSG(false, "Survived invocation of test syscall");
@@ -137,7 +138,8 @@ TEST(SeccompTest, RetKillOnlyKillsOneThread) {
pid_t const pid = fork();
if (pid == 0) {
// Register a signal handler for SIGSYS that we don't expect to be invoked.
- RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); });
+ RegisterSignalHandler(
+ SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); });
ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL);
// Pass CLONE_VFORK to block the original thread in the child process until
// the clone thread exits with SIGSYS.
@@ -351,7 +353,8 @@ TEST(SeccompTest, LeastPermissiveFilterReturnValueApplies) {
// one that causes the kill that should be ignored.
pid_t const pid = fork();
if (pid == 0) {
- RegisterSignalHandler(SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); });
+ RegisterSignalHandler(
+ SIGSYS, +[](int, siginfo_t*, void*) { _exit(1); });
ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_TRACE);
ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_KILL);
ApplySeccompFilter(kFilteredSyscall, SECCOMP_RET_ERRNO | ENOTNAM);
diff --git a/test/syscalls/linux/sendfile_socket.cc b/test/syscalls/linux/sendfile_socket.cc
index 3331288b7..8f7ee4163 100644
--- a/test/syscalls/linux/sendfile_socket.cc
+++ b/test/syscalls/linux/sendfile_socket.cc
@@ -41,15 +41,15 @@ class SendFileTest : public ::testing::TestWithParam<int> {
struct sockaddr server_addr = {};
switch (family) {
case AF_INET: {
- struct sockaddr_in *server_addr_in =
- reinterpret_cast<struct sockaddr_in *>(&server_addr);
+ struct sockaddr_in* server_addr_in =
+ reinterpret_cast<struct sockaddr_in*>(&server_addr);
server_addr_in->sin_family = family;
server_addr_in->sin_addr.s_addr = INADDR_ANY;
break;
}
case AF_UNIX: {
- struct sockaddr_un *server_addr_un =
- reinterpret_cast<struct sockaddr_un *>(&server_addr);
+ struct sockaddr_un* server_addr_un =
+ reinterpret_cast<struct sockaddr_un*>(&server_addr);
server_addr_un->sun_family = family;
server_addr_un->sun_path[0] = '\0';
break;
diff --git a/test/syscalls/linux/sigaction.cc b/test/syscalls/linux/sigaction.cc
index 9a53fd3e0..9d9dd57a8 100644
--- a/test/syscalls/linux/sigaction.cc
+++ b/test/syscalls/linux/sigaction.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <signal.h>
+#include <sys/syscall.h>
#include "gtest/gtest.h"
#include "test/util/test_util.h"
@@ -23,45 +24,53 @@ namespace testing {
namespace {
TEST(SigactionTest, GetLessThanOrEqualToZeroFails) {
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- ASSERT_THAT(sigaction(-1, NULL, &act), SyscallFailsWithErrno(EINVAL));
- ASSERT_THAT(sigaction(0, NULL, &act), SyscallFailsWithErrno(EINVAL));
+ struct sigaction act = {};
+ ASSERT_THAT(sigaction(-1, nullptr, &act), SyscallFailsWithErrno(EINVAL));
+ ASSERT_THAT(sigaction(0, nullptr, &act), SyscallFailsWithErrno(EINVAL));
}
TEST(SigactionTest, SetLessThanOrEqualToZeroFails) {
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- ASSERT_THAT(sigaction(0, &act, NULL), SyscallFailsWithErrno(EINVAL));
- ASSERT_THAT(sigaction(0, &act, NULL), SyscallFailsWithErrno(EINVAL));
+ struct sigaction act = {};
+ ASSERT_THAT(sigaction(0, &act, nullptr), SyscallFailsWithErrno(EINVAL));
+ ASSERT_THAT(sigaction(0, &act, nullptr), SyscallFailsWithErrno(EINVAL));
}
TEST(SigactionTest, GetGreaterThanMaxFails) {
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- ASSERT_THAT(sigaction(SIGRTMAX + 1, NULL, &act),
+ struct sigaction act = {};
+ ASSERT_THAT(sigaction(SIGRTMAX + 1, nullptr, &act),
SyscallFailsWithErrno(EINVAL));
}
TEST(SigactionTest, SetGreaterThanMaxFails) {
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- ASSERT_THAT(sigaction(SIGRTMAX + 1, &act, NULL),
+ struct sigaction act = {};
+ ASSERT_THAT(sigaction(SIGRTMAX + 1, &act, nullptr),
SyscallFailsWithErrno(EINVAL));
}
TEST(SigactionTest, SetSigkillFails) {
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- ASSERT_THAT(sigaction(SIGKILL, NULL, &act), SyscallSucceeds());
- ASSERT_THAT(sigaction(SIGKILL, &act, NULL), SyscallFailsWithErrno(EINVAL));
+ struct sigaction act = {};
+ ASSERT_THAT(sigaction(SIGKILL, nullptr, &act), SyscallSucceeds());
+ ASSERT_THAT(sigaction(SIGKILL, &act, nullptr), SyscallFailsWithErrno(EINVAL));
}
TEST(SigactionTest, SetSigstopFails) {
- struct sigaction act;
- memset(&act, 0, sizeof(act));
- ASSERT_THAT(sigaction(SIGSTOP, NULL, &act), SyscallSucceeds());
- ASSERT_THAT(sigaction(SIGSTOP, &act, NULL), SyscallFailsWithErrno(EINVAL));
+ struct sigaction act = {};
+ ASSERT_THAT(sigaction(SIGSTOP, nullptr, &act), SyscallSucceeds());
+ ASSERT_THAT(sigaction(SIGSTOP, &act, nullptr), SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(SigactionTest, BadSigsetFails) {
+ constexpr size_t kWrongSigSetSize = 43;
+
+ struct sigaction act = {};
+
+ // The syscall itself (rather than the libc wrapper) takes the sigset_t size.
+ ASSERT_THAT(
+ syscall(SYS_rt_sigaction, SIGTERM, nullptr, &act, kWrongSigSetSize),
+ SyscallFailsWithErrno(EINVAL));
+ ASSERT_THAT(
+ syscall(SYS_rt_sigaction, SIGTERM, &act, nullptr, kWrongSigSetSize),
+ SyscallFailsWithErrno(EINVAL));
}
} // namespace
diff --git a/test/syscalls/linux/sigaltstack.cc b/test/syscalls/linux/sigaltstack.cc
index a778fa639..24e7c4960 100644
--- a/test/syscalls/linux/sigaltstack.cc
+++ b/test/syscalls/linux/sigaltstack.cc
@@ -114,7 +114,7 @@ TEST(SigaltstackTest, ResetByExecve) {
volatile bool badhandler_on_sigaltstack = true; // Set by the handler.
char* volatile badhandler_low_water_mark = nullptr; // Set by the handler.
-volatile uint8_t badhandler_recursive_faults = 0; // Consumed by the handler.
+volatile uint8_t badhandler_recursive_faults = 0; // Consumed by the handler.
void badhandler(int sig, siginfo_t* siginfo, void* arg) {
char stack_var = 0;
@@ -168,8 +168,8 @@ TEST(SigaltstackTest, WalksOffBottom) {
// Trigger a single fault.
badhandler_low_water_mark =
- static_cast<char*>(stack.ss_sp) + SIGSTKSZ; // Expected top.
- badhandler_recursive_faults = 0; // Disable refault.
+ static_cast<char*>(stack.ss_sp) + SIGSTKSZ; // Expected top.
+ badhandler_recursive_faults = 0; // Disable refault.
Fault();
EXPECT_TRUE(badhandler_on_sigaltstack);
EXPECT_THAT(sigaltstack(nullptr, &stack), SyscallSucceeds());
diff --git a/test/syscalls/linux/sigiret.cc b/test/syscalls/linux/sigiret.cc
index a47c781ea..4deb1ae95 100644
--- a/test/syscalls/linux/sigiret.cc
+++ b/test/syscalls/linux/sigiret.cc
@@ -78,8 +78,8 @@ TEST(SigIretTest, CheckRcxR11) {
"1: pause; cmpl $0, %[gotvtalrm]; je 1b;" // while (!gotvtalrm);
"movq %%rcx, %[rcx];" // rcx = %rcx
"movq %%r11, %[r11];" // r11 = %r11
- : [ready] "=m"(ready), [rcx] "+m"(rcx), [r11] "+m"(r11)
- : [gotvtalrm] "m"(gotvtalrm)
+ : [ ready ] "=m"(ready), [ rcx ] "+m"(rcx), [ r11 ] "+m"(r11)
+ : [ gotvtalrm ] "m"(gotvtalrm)
: "cc", "memory", "rcx", "r11");
// If sigreturn(2) returns via 'sysret' then %rcx and %r11 will be
diff --git a/test/syscalls/linux/signalfd.cc b/test/syscalls/linux/signalfd.cc
index 09ecad34a..95be4b66c 100644
--- a/test/syscalls/linux/signalfd.cc
+++ b/test/syscalls/linux/signalfd.cc
@@ -39,6 +39,7 @@ namespace testing {
namespace {
constexpr int kSigno = SIGUSR1;
+constexpr int kSignoMax = 64; // SIGRTMAX
constexpr int kSignoAlt = SIGUSR2;
// Returns a new signalfd.
@@ -51,41 +52,45 @@ inline PosixErrorOr<FileDescriptor> NewSignalFD(sigset_t* mask, int flags = 0) {
return FileDescriptor(fd);
}
-TEST(Signalfd, Basic) {
+class SignalfdTest : public ::testing::TestWithParam<int> {};
+
+TEST_P(SignalfdTest, Basic) {
+ int signo = GetParam();
// Create the signalfd.
sigset_t mask;
sigemptyset(&mask);
- sigaddset(&mask, kSigno);
+ 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, kSigno));
- ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds());
+ 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, kSigno);
+ EXPECT_EQ(rbuf.ssi_signo, signo);
}
-TEST(Signalfd, MaskWorks) {
+TEST_P(SignalfdTest, MaskWorks) {
+ int signo = GetParam();
// Create two signalfds with different masks.
sigset_t mask1, mask2;
sigemptyset(&mask1);
sigemptyset(&mask2);
- sigaddset(&mask1, kSigno);
+ 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, kSigno));
+ 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(), kSigno), SyscallSucceeds());
+ ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds());
ASSERT_THAT(tgkill(getpid(), gettid(), kSignoAlt), SyscallSucceeds());
// We should see the signals on the appropriate signalfds.
@@ -98,7 +103,7 @@ TEST(Signalfd, MaskWorks) {
EXPECT_EQ(rbuf2.ssi_signo, kSignoAlt);
ASSERT_THAT(read(fd1.get(), &rbuf1, sizeof(rbuf1)),
SyscallSucceedsWithValue(sizeof(rbuf1)));
- EXPECT_EQ(rbuf1.ssi_signo, kSigno);
+ EXPECT_EQ(rbuf1.ssi_signo, signo);
}
TEST(Signalfd, Cloexec) {
@@ -111,11 +116,12 @@ TEST(Signalfd, Cloexec) {
EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC));
}
-TEST(Signalfd, Blocking) {
+TEST_P(SignalfdTest, Blocking) {
+ int signo = GetParam();
// Create the signalfd in blocking mode.
sigset_t mask;
sigemptyset(&mask);
- sigaddset(&mask, kSigno);
+ sigaddset(&mask, signo);
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, 0));
// Shared tid variable.
@@ -136,7 +142,7 @@ TEST(Signalfd, Blocking) {
struct signalfd_siginfo rbuf;
ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)),
SyscallSucceedsWithValue(sizeof(rbuf)));
- EXPECT_EQ(rbuf.ssi_signo, kSigno);
+ EXPECT_EQ(rbuf.ssi_signo, signo);
});
// Wait until blocked.
@@ -149,20 +155,21 @@ TEST(Signalfd, Blocking) {
//
// See gvisor.dev/issue/139.
if (IsRunningOnGvisor()) {
- ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds());
+ ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds());
} else {
- ASSERT_THAT(tgkill(getpid(), tid, kSigno), SyscallSucceeds());
+ ASSERT_THAT(tgkill(getpid(), tid, signo), SyscallSucceeds());
}
// Ensure that it was received.
t.Join();
}
-TEST(Signalfd, ThreadGroup) {
+TEST_P(SignalfdTest, ThreadGroup) {
+ int signo = GetParam();
// Create the signalfd in blocking mode.
sigset_t mask;
sigemptyset(&mask);
- sigaddset(&mask, kSigno);
+ sigaddset(&mask, signo);
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, 0));
// Shared variable.
@@ -176,7 +183,7 @@ TEST(Signalfd, ThreadGroup) {
struct signalfd_siginfo rbuf;
ASSERT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)),
SyscallSucceedsWithValue(sizeof(rbuf)));
- EXPECT_EQ(rbuf.ssi_signo, kSigno);
+ EXPECT_EQ(rbuf.ssi_signo, signo);
// Wait for the other thread.
absl::MutexLock ml(&mu);
@@ -185,7 +192,7 @@ TEST(Signalfd, ThreadGroup) {
});
// Deliver the signal to the threadgroup.
- ASSERT_THAT(kill(getpid(), kSigno), SyscallSucceeds());
+ ASSERT_THAT(kill(getpid(), signo), SyscallSucceeds());
// Wait for the first thread to process.
{
@@ -194,13 +201,13 @@ TEST(Signalfd, ThreadGroup) {
}
// Deliver to the thread group again (other thread still exists).
- ASSERT_THAT(kill(getpid(), kSigno), SyscallSucceeds());
+ 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, kSigno);
+ EXPECT_EQ(rbuf.ssi_signo, signo);
// Mark the test as done.
{
@@ -212,11 +219,12 @@ TEST(Signalfd, ThreadGroup) {
t.Join();
}
-TEST(Signalfd, Nonblock) {
+TEST_P(SignalfdTest, Nonblock) {
+ int signo = GetParam();
// Create the signalfd in non-blocking mode.
sigset_t mask;
sigemptyset(&mask);
- sigaddset(&mask, kSigno);
+ sigaddset(&mask, signo);
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(NewSignalFD(&mask, SFD_NONBLOCK));
@@ -227,20 +235,21 @@ TEST(Signalfd, Nonblock) {
// Block and deliver the signal.
const auto scoped_sigmask =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, kSigno));
- ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds());
+ 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, kSigno);
+ EXPECT_EQ(rbuf.ssi_signo, signo);
// Should block again.
EXPECT_THAT(read(fd.get(), &rbuf, sizeof(rbuf)),
SyscallFailsWithErrno(EWOULDBLOCK));
}
-TEST(Signalfd, SetMask) {
+TEST_P(SignalfdTest, SetMask) {
+ int signo = GetParam();
// Create the signalfd matching nothing.
sigset_t mask;
sigemptyset(&mask);
@@ -249,8 +258,8 @@ TEST(Signalfd, SetMask) {
// Block and deliver a signal.
const auto scoped_sigmask =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, kSigno));
- ASSERT_THAT(tgkill(getpid(), gettid(), kSigno), SyscallSucceeds());
+ ASSERT_NO_ERRNO_AND_VALUE(ScopedSignalMask(SIG_BLOCK, signo));
+ ASSERT_THAT(tgkill(getpid(), gettid(), signo), SyscallSucceeds());
// We should have nothing.
struct signalfd_siginfo rbuf;
@@ -258,29 +267,30 @@ TEST(Signalfd, SetMask) {
SyscallFailsWithErrno(EWOULDBLOCK));
// Change the signal mask.
- sigaddset(&mask, kSigno);
+ 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, kSigno);
+ EXPECT_EQ(rbuf.ssi_signo, signo);
}
-TEST(Signalfd, Poll) {
+TEST_P(SignalfdTest, Poll) {
+ int signo = GetParam();
// Create the signalfd.
sigset_t mask;
sigemptyset(&mask);
- sigaddset(&mask, kSigno);
+ 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, kSigno));
+ 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, kSigno), SyscallSucceeds());
+ ASSERT_THAT(tgkill(getpid(), orig_tid, signo), SyscallSucceeds());
});
// Start polling for the signal. We expect that it is not available at the
@@ -297,19 +307,18 @@ TEST(Signalfd, Poll) {
SyscallSucceedsWithValue(sizeof(rbuf)));
}
-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), "");
+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;
@@ -328,6 +337,20 @@ TEST(Signalfd, Ppoll) {
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
@@ -340,6 +363,7 @@ int main(int argc, char** argv) {
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);
diff --git a/test/syscalls/linux/socket_abstract.cc b/test/syscalls/linux/socket_abstract.cc
index 715d87b76..00999f192 100644
--- a/test/syscalls/linux/socket_abstract.cc
+++ b/test/syscalls/linux/socket_abstract.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
@@ -43,5 +44,6 @@ 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_distribution.cc b/test/syscalls/linux/socket_bind_to_device_distribution.cc
index 5767181a1..5ed57625c 100644
--- a/test/syscalls/linux/socket_bind_to_device_distribution.cc
+++ b/test/syscalls/linux/socket_bind_to_device_distribution.cc
@@ -183,7 +183,14 @@ TEST_P(BindToDeviceDistributionTest, Tcp) {
}
// Receive some data from a socket to be sure that the connect()
// system call has been completed on another side.
- int data;
+ // 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)));
@@ -198,15 +205,29 @@ TEST_P(BindToDeviceDistributionTest, Tcp) {
}
for (int i = 0; i < kConnectAttempts; i++) {
- FileDescriptor const fd = ASSERT_NO_ERRNO_AND_VALUE(
+ 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());
+ // Do two separate sends to ensure two segments are received. This is
+ // required for netstack where read is incorrectly assuming a whole
+ // segment is read when endpoint.Read() is called which is technically
+ // incorrect as the syscall that invoked endpoint.Read() may only
+ // consume it partially. This results in a case where a close() of
+ // such a socket does not trigger a RST in netstack due to the
+ // endpoint assuming that the endpoint has no unread data.
EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0),
SyscallSucceedsWithValue(sizeof(i)));
+
+ // TODO(gvisor.dev/issue/1449): Remove this block once netstack correctly
+ // generates a RST.
+ if (IsRunningOnGvisor()) {
+ EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0),
+ SyscallSucceedsWithValue(sizeof(i)));
+ }
}
// Join threads to be sure that all connections have been counted.
diff --git a/test/syscalls/linux/socket_bind_to_device_sequence.cc b/test/syscalls/linux/socket_bind_to_device_sequence.cc
index 34b1058a9..637d1151a 100644
--- a/test/syscalls/linux/socket_bind_to_device_sequence.cc
+++ b/test/syscalls/linux/socket_bind_to_device_sequence.cc
@@ -66,7 +66,7 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
// Gets a device by device_id. If the device_id has been seen before, returns
// the previously returned device. If not, finds or creates a new device.
// Returns an empty string on failure.
- void GetDevice(int device_id, string *device_name) {
+ void GetDevice(int device_id, string* device_name) {
auto device = devices_.find(device_id);
if (device != devices_.end()) {
*device_name = device->second;
@@ -112,7 +112,7 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
// Sets the socket_id to uniquely identify the socket bound if it is not
// nullptr.
void BindSocket(bool reuse_port, bool reuse_addr, int device_id = 0,
- int want = 0, int *socket_id = nullptr) {
+ int want = 0, int* socket_id = nullptr) {
next_socket_id_++;
sockets_to_close_[next_socket_id_] = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto socket_fd = sockets_to_close_[next_socket_id_]->get();
@@ -154,12 +154,12 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
addr.sin_port = port_;
if (want == 0) {
ASSERT_THAT(
- bind(socket_fd, reinterpret_cast<const struct sockaddr *>(&addr),
+ bind(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(addr)),
SyscallSucceeds());
} else {
ASSERT_THAT(
- bind(socket_fd, reinterpret_cast<const struct sockaddr *>(&addr),
+ bind(socket_fd, reinterpret_cast<const struct sockaddr*>(&addr),
sizeof(addr)),
SyscallFailsWithErrno(want));
}
@@ -169,7 +169,7 @@ class BindToDeviceSequenceTest : public ::testing::TestWithParam<SocketKind> {
// remember it for future commands.
socklen_t addr_size = sizeof(addr);
ASSERT_THAT(
- getsockname(socket_fd, reinterpret_cast<struct sockaddr *>(&addr),
+ getsockname(socket_fd, reinterpret_cast<struct sockaddr*>(&addr),
&addr_size),
SyscallSucceeds());
port_ = addr.sin_port;
diff --git a/test/syscalls/linux/socket_filesystem.cc b/test/syscalls/linux/socket_filesystem.cc
index 74e262959..287359363 100644
--- a/test/syscalls/linux/socket_filesystem.cc
+++ b/test/syscalls/linux/socket_filesystem.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
@@ -43,5 +44,6 @@ INSTANTIATE_TEST_SUITE_P(
FilesystemUnixSockets, UnixSocketPairCmsgTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index 619d41901..3bf7081b9 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -32,6 +32,7 @@
#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"
@@ -102,6 +103,161 @@ TEST(BadSocketPairArgs, ValidateErrForBadCallsToSocketPair) {
SyscallFailsWithErrno(EAFNOSUPPORT));
}
+enum class Operation {
+ Bind,
+ Connect,
+ SendTo,
+};
+
+std::string OperationToString(Operation operation) {
+ switch (operation) {
+ case Operation::Bind:
+ return "Bind";
+ case Operation::Connect:
+ return "Connect";
+ case Operation::SendTo:
+ 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(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/issues/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.
@@ -377,7 +533,7 @@ TEST_P(SocketInetLoopbackTest, TCPFinWait2Test_NoRandomSave) {
// Sleep for a little over the linger timeout to reduce flakiness in
// save/restore tests.
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1));
+ absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2));
ds.reset();
@@ -672,6 +828,164 @@ TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
EXPECT_EQ(get, kUserTimeout);
}
+// TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
+// saved. Enable S/R once issue is fixed.
+TEST_P(SocketInetLoopbackTest, TCPDeferAccept_NoRandomSave) {
+ // TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
+ // saved. Enable S/R issue is fixed.
+ DisableSave ds;
+
+ auto const& param = GetParam();
+ TestAddress const& listener = param.listener;
+ TestAddress const& connector = param.connector;
+
+ // Create the listening socket.
+ const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
+ listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(),
+ reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ const uint16_t port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Set the TCP_DEFER_ACCEPT on the listening socket.
+ constexpr int kTCPDeferAccept = 3;
+ ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT,
+ &kTCPDeferAccept, sizeof(kTCPDeferAccept)),
+ SyscallSucceeds());
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
+ reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Set the listening socket to nonblock so that we can verify that there is no
+ // connection in queue despite the connect above succeeding since the peer has
+ // sent no data and TCP_DEFER_ACCEPT is set on the listening socket. Set the
+ // FD to O_NONBLOCK.
+ int opts;
+ ASSERT_THAT(opts = fcntl(listen_fd.get(), F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
+
+ ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr),
+ SyscallFailsWithErrno(EWOULDBLOCK));
+
+ // Set FD back to blocking.
+ opts &= ~O_NONBLOCK;
+ ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
+
+ // Now write some data to the socket.
+ int data = 0;
+ ASSERT_THAT(RetryEINTR(write)(conn_fd.get(), &data, sizeof(data)),
+ SyscallSucceedsWithValue(sizeof(data)));
+
+ // This should now cause the connection to complete and be delivered to the
+ // accept socket.
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Verify that the accepted socket returns the data written.
+ int get = -1;
+ ASSERT_THAT(RetryEINTR(recv)(accepted.get(), &get, sizeof(get), 0),
+ SyscallSucceedsWithValue(sizeof(get)));
+
+ EXPECT_EQ(get, data);
+}
+
+// TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
+// saved. Enable S/R once issue is fixed.
+TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout_NoRandomSave) {
+ // TODO(gvisor.dev/issue/1688): Partially completed passive endpoints are not
+ // saved. Enable S/R once issue is fixed.
+ DisableSave ds;
+
+ auto const& param = GetParam();
+ TestAddress const& listener = param.listener;
+ TestAddress const& connector = param.connector;
+
+ // Create the listening socket.
+ const FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ sockaddr_storage listen_addr = listener.addr;
+ ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr),
+ listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Get the port bound by the listening socket.
+ socklen_t addrlen = listener.addr_len;
+ ASSERT_THAT(getsockname(listen_fd.get(),
+ reinterpret_cast<sockaddr*>(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ const uint16_t port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Set the TCP_DEFER_ACCEPT on the listening socket.
+ constexpr int kTCPDeferAccept = 3;
+ ASSERT_THAT(setsockopt(listen_fd.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT,
+ &kTCPDeferAccept, sizeof(kTCPDeferAccept)),
+ SyscallSucceeds());
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(),
+ reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Set the listening socket to nonblock so that we can verify that there is no
+ // connection in queue despite the connect above succeeding since the peer has
+ // sent no data and TCP_DEFER_ACCEPT is set on the listening socket. Set the
+ // FD to O_NONBLOCK.
+ int opts;
+ ASSERT_THAT(opts = fcntl(listen_fd.get(), F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
+
+ // Verify that there is no acceptable connection before TCP_DEFER_ACCEPT
+ // timeout is hit.
+ absl::SleepFor(absl::Seconds(kTCPDeferAccept - 1));
+ ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr),
+ SyscallFailsWithErrno(EWOULDBLOCK));
+
+ // Set FD back to blocking.
+ opts &= ~O_NONBLOCK;
+ ASSERT_THAT(fcntl(listen_fd.get(), F_SETFL, opts), SyscallSucceeds());
+
+ // Now sleep for a little over the TCP_DEFER_ACCEPT duration. When the timeout
+ // is hit a SYN-ACK should be retransmitted by the listener as a last ditch
+ // attempt to complete the connection with or without data.
+ absl::SleepFor(absl::Seconds(2));
+
+ // Verify that we have a connection that can be accepted even though no
+ // data was written.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+}
+
INSTANTIATE_TEST_SUITE_P(
All, SocketInetLoopbackTest,
::testing::Values(
@@ -714,7 +1028,7 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread_NoRandomSave) {
sockaddr_storage listen_addr = listener.addr;
sockaddr_storage conn_addr = connector.addr;
constexpr int kThreadCount = 3;
- constexpr int kConnectAttempts = 4096;
+ constexpr int kConnectAttempts = 10000;
// Create the listening socket.
FileDescriptor listener_fds[kThreadCount];
@@ -729,7 +1043,7 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread_NoRandomSave) {
ASSERT_THAT(
bind(fd, reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len),
SyscallSucceeds());
- ASSERT_THAT(listen(fd, kConnectAttempts / 3), SyscallSucceeds());
+ ASSERT_THAT(listen(fd, 40), SyscallSucceeds());
// On the first bind we need to determine which port was bound.
if (i != 0) {
@@ -772,7 +1086,14 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread_NoRandomSave) {
}
// Receive some data from a socket to be sure that the connect()
// system call has been completed on another side.
- int data;
+ // 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)));
@@ -795,8 +1116,22 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread_NoRandomSave) {
connector.addr_len),
SyscallSucceeds());
+ // Do two separate sends to ensure two segments are received. This is
+ // required for netstack where read is incorrectly assuming a whole
+ // segment is read when endpoint.Read() is called which is technically
+ // incorrect as the syscall that invoked endpoint.Read() may only
+ // consume it partially. This results in a case where a close() of
+ // such a socket does not trigger a RST in netstack due to the
+ // endpoint assuming that the endpoint has no unread data.
EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0),
SyscallSucceedsWithValue(sizeof(i)));
+
+ // TODO(gvisor.dev/issue/1449): Remove this block once netstack correctly
+ // generates a RST.
+ if (IsRunningOnGvisor()) {
+ EXPECT_THAT(RetryEINTR(send)(fd.get(), &i, sizeof(i), 0),
+ SyscallSucceedsWithValue(sizeof(i)));
+ }
}
});
diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc
index 57ce8e169..27779e47c 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic.cc
@@ -24,6 +24,7 @@
#include <sys/un.h>
#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/socket_test_util.h"
@@ -875,5 +876,37 @@ TEST_P(TCPSocketPairTest, SetTCPUserTimeoutAboveZero) {
EXPECT_EQ(get, kAbove);
}
+TEST_P(TCPSocketPairTest, TCPResetDuringClose_NoRandomSave) {
+ DisableSave ds; // Too many syscalls.
+ constexpr int kThreadCount = 1000;
+ std::unique_ptr<ScopedThread> instances[kThreadCount];
+ for (int i = 0; i < kThreadCount; i++) {
+ instances[i] = absl::make_unique<ScopedThread>([&]() {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ ScopedThread t([&]() {
+ // Close one end to trigger sending of a FIN.
+ struct pollfd poll_fd = {sockets->second_fd(), POLLIN | POLLHUP, 0};
+ // Wait up to 20 seconds for the data.
+ constexpr int kPollTimeoutMs = 20000;
+ ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
+ SyscallSucceedsWithValue(1));
+ ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
+ });
+
+ // Send some data then close.
+ constexpr char kStr[] = "abc";
+ ASSERT_THAT(write(sockets->first_fd(), kStr, 3),
+ SyscallSucceedsWithValue(3));
+ absl::SleepFor(absl::Milliseconds(10));
+ ASSERT_THAT(close(sockets->release_first_fd()), SyscallSucceeds());
+ t.Join();
+ });
+ }
+ for (int i = 0; i < kThreadCount; i++) {
+ instances[i]->Join();
+ }
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc
index d11f7cc23..4e79d21f4 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVecToVec<SocketPairKind>(
@@ -39,5 +40,6 @@ 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_blocking.cc b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc
index fcd20102f..f996b93d2 100644
--- a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc
+++ b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVecToVec<SocketPairKind>(
@@ -39,5 +40,6 @@ 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
index 63a05b799..ffa377210 100644
--- a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc
+++ b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVecToVec<SocketPairKind>(
@@ -38,5 +39,6 @@ INSTANTIATE_TEST_SUITE_P(
NonBlockingTCPSockets, NonBlockingSocketPairTest,
::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
index 66eb68857..53290bed7 100644
--- a/test/syscalls/linux/socket_ip_udp_generic.cc
+++ b/test/syscalls/linux/socket_ip_udp_generic.cc
@@ -209,6 +209,46 @@ TEST_P(UDPSocketPairTest, SetMulticastLoopChar) {
EXPECT_EQ(get, kSockOptOn);
}
+// Ensure that Receiving TOS is off by default.
+TEST_P(UDPSocketPairTest, RecvTosDefault) {
+ 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_RECVTOS, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kSockOptOff);
+}
+
+// Test that setting and getting IP_RECVTOS works as expected.
+TEST_P(UDPSocketPairTest, SetRecvTos) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS,
+ &kSockOptOff, sizeof(kSockOptOff)),
+ SyscallSucceeds());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kSockOptOff);
+
+ ASSERT_THAT(setsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(
+ getsockopt(sockets->first_fd(), IPPROTO_IP, IP_RECVTOS, &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());
diff --git a/test/syscalls/linux/socket_ip_udp_loopback.cc b/test/syscalls/linux/socket_ip_udp_loopback.cc
index 1df74a348..c7fa44884 100644
--- a/test/syscalls/linux/socket_ip_udp_loopback.cc
+++ b/test/syscalls/linux/socket_ip_udp_loopback.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return {
@@ -44,5 +45,6 @@ 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
index 1e259efa7..d6925a8df 100644
--- a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc
+++ b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return {
@@ -33,5 +34,6 @@ 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
index 74cbd326d..d675eddc6 100644
--- a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc
+++ b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return {
@@ -33,5 +34,6 @@ INSTANTIATE_TEST_SUITE_P(
NonBlockingUDPSockets, NonBlockingSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ip_unbound.cc b/test/syscalls/linux/socket_ip_unbound.cc
index b6754111f..ca597e267 100644
--- a/test/syscalls/linux/socket_ip_unbound.cc
+++ b/test/syscalls/linux/socket_ip_unbound.cc
@@ -129,6 +129,7 @@ TEST_P(IPUnboundSocketTest, InvalidNegativeTtl) {
struct TOSOption {
int level;
int option;
+ int cmsg_level;
};
constexpr int INET_ECN_MASK = 3;
@@ -139,10 +140,12 @@ static TOSOption GetTOSOption(int 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;
@@ -386,6 +389,36 @@ TEST_P(IPUnboundSocketTest, NullTOS) {
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));
+}
+
INSTANTIATE_TEST_SUITE_P(
IPUnboundSockets, IPUnboundSocketTest,
::testing::ValuesIn(VecCat<SocketKind>(VecCat<SocketKind>(
diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc
index 405dbbd73..15d4b85a7 100644
--- a/test/syscalls/linux/socket_netdevice.cc
+++ b/test/syscalls/linux/socket_netdevice.cc
@@ -91,7 +91,7 @@ TEST(NetdeviceTest, Netmask) {
int prefixlen = -1;
ASSERT_NO_ERRNO(NetlinkRequestResponse(
fd, &req, sizeof(req),
- [&](const struct nlmsghdr *hdr) {
+ [&](const struct nlmsghdr* hdr) {
EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE)));
EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
@@ -107,8 +107,8 @@ TEST(NetdeviceTest, Netmask) {
// RTM_NEWADDR contains at least the header and ifaddrmsg.
EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg));
- struct ifaddrmsg *ifaddrmsg =
- reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr));
+ struct ifaddrmsg* ifaddrmsg =
+ reinterpret_cast<struct ifaddrmsg*>(NLMSG_DATA(hdr));
if (ifaddrmsg->ifa_index == static_cast<uint32_t>(ifr.ifr_ifindex) &&
ifaddrmsg->ifa_family == AF_INET) {
prefixlen = ifaddrmsg->ifa_prefixlen;
@@ -127,8 +127,8 @@ TEST(NetdeviceTest, Netmask) {
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
ASSERT_THAT(ioctl(sock.get(), SIOCGIFNETMASK, &ifr), SyscallSucceeds());
EXPECT_EQ(ifr.ifr_netmask.sa_family, AF_INET);
- struct sockaddr_in *sin =
- reinterpret_cast<struct sockaddr_in *>(&ifr.ifr_netmask);
+ struct sockaddr_in* sin =
+ reinterpret_cast<struct sockaddr_in*>(&ifr.ifr_netmask);
EXPECT_EQ(sin->sin_addr.s_addr, mask);
}
diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc
index ef567f512..1e28e658d 100644
--- a/test/syscalls/linux/socket_netlink_route.cc
+++ b/test/syscalls/linux/socket_netlink_route.cc
@@ -442,6 +442,90 @@ TEST(NetlinkRouteTest, GetRouteDump) {
EXPECT_TRUE(dstFound);
}
+// GetRouteRequest tests a RTM_GETROUTE request with RTM_F_LOOKUP_TABLE flag.
+TEST(NetlinkRouteTest, GetRouteRequest) {
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
+ uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
+
+ struct __attribute__((__packed__)) request {
+ struct nlmsghdr hdr;
+ struct rtmsg rtm;
+ struct nlattr nla;
+ struct in_addr sin_addr;
+ };
+
+ constexpr uint32_t kSeq = 12345;
+
+ struct request req = {};
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_type = RTM_GETROUTE;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = kSeq;
+
+ req.rtm.rtm_family = AF_INET;
+ req.rtm.rtm_dst_len = 32;
+ req.rtm.rtm_src_len = 0;
+ req.rtm.rtm_tos = 0;
+ req.rtm.rtm_table = RT_TABLE_UNSPEC;
+ req.rtm.rtm_protocol = RTPROT_UNSPEC;
+ req.rtm.rtm_scope = RT_SCOPE_UNIVERSE;
+ req.rtm.rtm_type = RTN_UNSPEC;
+ req.rtm.rtm_flags = RTM_F_LOOKUP_TABLE;
+
+ req.nla.nla_len = 8;
+ req.nla.nla_type = RTA_DST;
+ inet_aton("127.0.0.2", &req.sin_addr);
+
+ bool rtDstFound = false;
+ ASSERT_NO_ERRNO(NetlinkRequestResponseSingle(
+ fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
+ // Validate the reponse to RTM_GETROUTE request with RTM_F_LOOKUP_TABLE
+ // flag.
+ EXPECT_THAT(hdr->nlmsg_type, RTM_NEWROUTE);
+
+ EXPECT_TRUE(hdr->nlmsg_flags == 0) << std::hex << hdr->nlmsg_flags;
+
+ EXPECT_EQ(hdr->nlmsg_seq, kSeq);
+ EXPECT_EQ(hdr->nlmsg_pid, port);
+
+ // RTM_NEWROUTE contains at least the header and rtmsg.
+ ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg)));
+ const struct rtmsg* msg =
+ reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr));
+
+ // NOTE: rtmsg fields are char fields.
+ std::cout << "Found route table=" << static_cast<int>(msg->rtm_table)
+ << ", protocol=" << static_cast<int>(msg->rtm_protocol)
+ << ", scope=" << static_cast<int>(msg->rtm_scope)
+ << ", type=" << static_cast<int>(msg->rtm_type);
+
+ EXPECT_EQ(msg->rtm_family, AF_INET);
+ EXPECT_EQ(msg->rtm_dst_len, 32);
+ EXPECT_TRUE((msg->rtm_flags & RTM_F_CLONED) == RTM_F_CLONED)
+ << std::hex << msg->rtm_flags;
+
+ int len = RTM_PAYLOAD(hdr);
+ std::cout << ", len=" << len;
+ for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len);
+ attr = RTA_NEXT(attr, len)) {
+ if (attr->rta_type == RTA_DST) {
+ char address[INET_ADDRSTRLEN] = {};
+ inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address));
+ std::cout << ", dst=" << address;
+ rtDstFound = true;
+ } else if (attr->rta_type == RTA_OIF) {
+ const char* oif = reinterpret_cast<const char*>(RTA_DATA(attr));
+ std::cout << ", oif=" << oif;
+ }
+ }
+
+ std::cout << std::endl;
+ }));
+ // Found RTA_DST for RTM_F_LOOKUP_TABLE.
+ EXPECT_TRUE(rtDstFound);
+}
+
// RecvmsgTrunc tests the recvmsg MSG_TRUNC flag with zero length output
// buffer. MSG_TRUNC with a zero length buffer should consume subsequent
// messages off the socket.
diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc
index 723f5d728..cd2212a1a 100644
--- a/test/syscalls/linux/socket_netlink_util.cc
+++ b/test/syscalls/linux/socket_netlink_util.cc
@@ -108,5 +108,43 @@ PosixError NetlinkRequestResponse(
return NoError();
}
+PosixError NetlinkRequestResponseSingle(
+ const FileDescriptor& fd, void* request, size_t len,
+ const std::function<void(const struct nlmsghdr* hdr)>& fn) {
+ struct iovec iov = {};
+ iov.iov_base = request;
+ iov.iov_len = len;
+
+ struct msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ // No destination required; it defaults to pid 0, the kernel.
+
+ RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0));
+
+ constexpr size_t kBufferSize = 4096;
+ std::vector<char> buf(kBufferSize);
+ iov.iov_base = buf.data();
+ iov.iov_len = buf.size();
+
+ int ret;
+ RETURN_ERROR_IF_SYSCALL_FAIL(ret = RetryEINTR(recvmsg)(fd.get(), &msg, 0));
+
+ // We don't bother with the complexity of dealing with truncated messages.
+ // We must allocate a large enough buffer up front.
+ if ((msg.msg_flags & MSG_TRUNC) == MSG_TRUNC) {
+ return PosixError(
+ EIO,
+ absl::StrCat("Received truncated message with flags: ", msg.msg_flags));
+ }
+
+ for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data());
+ NLMSG_OK(hdr, ret); hdr = NLMSG_NEXT(hdr, ret)) {
+ fn(hdr);
+ }
+
+ return NoError();
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h
index 76e772c48..3678c0599 100644
--- a/test/syscalls/linux/socket_netlink_util.h
+++ b/test/syscalls/linux/socket_netlink_util.h
@@ -32,12 +32,21 @@ PosixErrorOr<FileDescriptor> NetlinkBoundSocket(int protocol);
// Returns the port ID of the passed socket.
PosixErrorOr<uint32_t> NetlinkPortID(int fd);
-// Send the passed request and call fn will all response netlink messages.
+// Send the passed request and call fn on all response netlink messages.
+//
+// To be used on requests with NLM_F_MULTI reponses.
PosixError NetlinkRequestResponse(
const FileDescriptor& fd, void* request, size_t len,
const std::function<void(const struct nlmsghdr* hdr)>& fn,
bool expect_nlmsgerr);
+// Send the passed request and call fn on all response netlink messages.
+//
+// To be used on requests without NLM_F_MULTI reponses.
+PosixError NetlinkRequestResponseSingle(
+ const FileDescriptor& fd, void* request, size_t len,
+ const std::function<void(const struct nlmsghdr* hdr)>& fn);
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_non_stream.cc b/test/syscalls/linux/socket_non_stream.cc
index d91c5ed39..c61817f14 100644
--- a/test/syscalls/linux/socket_non_stream.cc
+++ b/test/syscalls/linux/socket_non_stream.cc
@@ -113,7 +113,7 @@ TEST_P(NonStreamSocketPairTest, RecvmsgMsghdrFlagMsgTrunc) {
EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(received_data)));
// Check that msghdr flags were updated.
- EXPECT_EQ(msg.msg_flags, MSG_TRUNC);
+ EXPECT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC);
}
// Stream sockets allow data sent with multiple sends to be peeked at in a
@@ -193,7 +193,7 @@ TEST_P(NonStreamSocketPairTest, MsgTruncTruncationRecvmsgMsghdrFlagMsgTrunc) {
EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(received_data)));
// Check that msghdr flags were updated.
- EXPECT_EQ(msg.msg_flags, MSG_TRUNC);
+ EXPECT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC);
}
TEST_P(NonStreamSocketPairTest, MsgTruncSameSize) {
@@ -224,5 +224,114 @@ TEST_P(NonStreamSocketPairTest, MsgTruncNotFull) {
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_blocking.cc b/test/syscalls/linux/socket_non_stream_blocking.cc
index 62d87c1af..b052f6e61 100644
--- a/test/syscalls/linux/socket_non_stream_blocking.cc
+++ b/test/syscalls/linux/socket_non_stream_blocking.cc
@@ -25,6 +25,7 @@
#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 {
@@ -44,5 +45,41 @@ TEST_P(BlockingNonStreamSocketPairTest, RecvLessThanBufferWaitAll) {
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_stream.cc b/test/syscalls/linux/socket_stream.cc
index 346443f96..6522b2e01 100644
--- a/test/syscalls/linux/socket_stream.cc
+++ b/test/syscalls/linux/socket_stream.cc
@@ -104,7 +104,60 @@ TEST_P(StreamSocketPairTest, RecvmsgMsghdrFlagsNoMsgTrunc) {
EXPECT_EQ(0, memcmp(received_data, sent_data, sizeof(received_data)));
// Check that msghdr flags were cleared (MSG_TRUNC was not set).
- EXPECT_EQ(msg.msg_flags, 0);
+ 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) {
diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc
index e9cc082bf..538ee2268 100644
--- a/test/syscalls/linux/socket_stream_blocking.cc
+++ b/test/syscalls/linux/socket_stream_blocking.cc
@@ -32,38 +32,38 @@ namespace gvisor {
namespace testing {
TEST_P(BlockingStreamSocketPairTest, BlockPartialWriteClosed) {
- // FIXME(b/35921550): gVisor doesn't support SO_SNDBUF on UDS, nor does it
- // enforce any limit; it will write arbitrary amounts of data without
- // blocking.
- SKIP_IF(IsRunningOnGvisor());
-
- auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
-
- int buffer_size;
- socklen_t length = sizeof(buffer_size);
- ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF,
- &buffer_size, &length),
- SyscallSucceeds());
-
- int wfd = sockets->first_fd();
- ScopedThread t([wfd, buffer_size]() {
- std::vector<char> buf(2 * buffer_size);
- // Write more than fits in the buffer. Blocks then returns partial write
- // when the other end is closed. The next call returns EPIPE.
- //
- // N.B. writes occur in chunks, so we may see less than buffer_size from
- // the first call.
- ASSERT_THAT(write(wfd, buf.data(), buf.size()),
- SyscallSucceedsWithValue(::testing::Gt(0)));
- ASSERT_THAT(write(wfd, buf.data(), buf.size()),
- ::testing::AnyOf(SyscallFailsWithErrno(EPIPE),
- SyscallFailsWithErrno(ECONNRESET)));
- });
-
- // Leave time for write to become blocked.
- absl::SleepFor(absl::Seconds(1));
-
- ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
+ // FIXME(b/35921550): gVisor doesn't support SO_SNDBUF on UDS, nor does it
+ // enforce any limit; it will write arbitrary amounts of data without
+ // blocking.
+ SKIP_IF(IsRunningOnGvisor());
+
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ int buffer_size;
+ socklen_t length = sizeof(buffer_size);
+ ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_SNDBUF,
+ &buffer_size, &length),
+ SyscallSucceeds());
+
+ int wfd = sockets->first_fd();
+ ScopedThread t([wfd, buffer_size]() {
+ std::vector<char> buf(2 * buffer_size);
+ // Write more than fits in the buffer. Blocks then returns partial write
+ // when the other end is closed. The next call returns EPIPE.
+ //
+ // N.B. writes occur in chunks, so we may see less than buffer_size from
+ // the first call.
+ ASSERT_THAT(write(wfd, buf.data(), buf.size()),
+ SyscallSucceedsWithValue(::testing::Gt(0)));
+ ASSERT_THAT(write(wfd, buf.data(), buf.size()),
+ ::testing::AnyOf(SyscallFailsWithErrno(EPIPE),
+ SyscallFailsWithErrno(ECONNRESET)));
+ });
+
+ // Leave time for write to become blocked.
+ absl::SleepFor(absl::Seconds(1));
+
+ ASSERT_THAT(close(sockets->release_second_fd()), SyscallSucceeds());
}
// Random save may interrupt the call to sendmsg() in SendLargeSendMsg(),
diff --git a/test/syscalls/linux/socket_unix_abstract_nonblock.cc b/test/syscalls/linux/socket_unix_abstract_nonblock.cc
index be31ab2a7..8bef76b67 100644
--- a/test/syscalls/linux/socket_unix_abstract_nonblock.cc
+++ b/test/syscalls/linux/socket_unix_abstract_nonblock.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
@@ -33,5 +34,6 @@ 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
index 6f84221b2..77cb8c6d6 100644
--- a/test/syscalls/linux/socket_unix_blocking_local.cc
+++ b/test/syscalls/linux/socket_unix_blocking_local.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return VecCat<SocketPairKind>(
@@ -39,5 +40,6 @@ INSTANTIATE_TEST_SUITE_P(
NonBlockingUnixDomainSockets, BlockingSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_unix_dgram_local.cc b/test/syscalls/linux/socket_unix_dgram_local.cc
index 9134fcdf7..31d2d5216 100644
--- a/test/syscalls/linux/socket_unix_dgram_local.cc
+++ b/test/syscalls/linux/socket_unix_dgram_local.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return VecCat<SocketPairKind>(VecCat<SocketPairKind>(
@@ -52,5 +53,6 @@ INSTANTIATE_TEST_SUITE_P(
DgramUnixSockets, NonStreamSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_unix_domain.cc b/test/syscalls/linux/socket_unix_domain.cc
index fa3efc7f8..f7dff8b4d 100644
--- a/test/syscalls/linux/socket_unix_domain.cc
+++ b/test/syscalls/linux/socket_unix_domain.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
@@ -33,5 +34,6 @@ 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
index 8ba7af971..6700b4d90 100644
--- a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc
+++ b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
@@ -33,5 +34,6 @@ 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_blocking_local.cc b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc
index 8855d5001..fddcdf1c5 100644
--- a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc
+++ b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return VecCat<SocketPairKind>(
@@ -36,5 +37,6 @@ 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
index 411fb4518..85999db04 100644
--- a/test/syscalls/linux/socket_unix_pair.cc
+++ b/test/syscalls/linux/socket_unix_pair.cc
@@ -22,6 +22,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return VecCat<SocketPairKind>(ApplyVec<SocketPairKind>(
@@ -38,5 +39,6 @@ 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
index 3135d325f..281410a9a 100644
--- a/test/syscalls/linux/socket_unix_pair_nonblock.cc
+++ b/test/syscalls/linux/socket_unix_pair_nonblock.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return ApplyVec<SocketPairKind>(
@@ -33,5 +34,6 @@ INSTANTIATE_TEST_SUITE_P(
NonBlockingUnixSockets, NonBlockingSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_unix_seqpacket_local.cc b/test/syscalls/linux/socket_unix_seqpacket_local.cc
index dff75a532..69a5f150d 100644
--- a/test/syscalls/linux/socket_unix_seqpacket_local.cc
+++ b/test/syscalls/linux/socket_unix_seqpacket_local.cc
@@ -23,6 +23,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return VecCat<SocketPairKind>(VecCat<SocketPairKind>(
@@ -52,5 +53,6 @@ INSTANTIATE_TEST_SUITE_P(
SeqpacketUnixSockets, UnixNonStreamSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // 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
index 08e579ba7..8429bd429 100644
--- a/test/syscalls/linux/socket_unix_stream_blocking_local.cc
+++ b/test/syscalls/linux/socket_unix_stream_blocking_local.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return {
@@ -34,5 +35,6 @@ 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
index 65eef1a81..a7e3449a9 100644
--- a/test/syscalls/linux/socket_unix_stream_local.cc
+++ b/test/syscalls/linux/socket_unix_stream_local.cc
@@ -21,6 +21,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return VecCat<SocketPairKind>(
@@ -42,5 +43,6 @@ 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
index 1936aa135..4b763c8e2 100644
--- a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc
+++ b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc
@@ -20,6 +20,7 @@
namespace gvisor {
namespace testing {
+namespace {
std::vector<SocketPairKind> GetSocketPairs() {
return {
@@ -33,5 +34,6 @@ INSTANTIATE_TEST_SUITE_P(
NonBlockingStreamUnixSockets, NonBlockingStreamSocketPairTest,
::testing::ValuesIn(IncludeReversals(GetSocketPairs())));
+} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc
index 85232cb1f..faa1247f6 100644
--- a/test/syscalls/linux/splice.cc
+++ b/test/syscalls/linux/splice.cc
@@ -60,6 +60,62 @@ TEST(SpliceTest, TwoRegularFiles) {
SyscallFailsWithErrno(EINVAL));
}
+int memfd_create(const std::string& name, unsigned int flags) {
+ return syscall(__NR_memfd_create, name.c_str(), flags);
+}
+
+TEST(SpliceTest, NegativeOffset) {
+ // Create a new pipe.
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ const FileDescriptor rfd(fds[0]);
+ const FileDescriptor wfd(fds[1]);
+
+ // Fill the pipe.
+ std::vector<char> buf(kPageSize);
+ RandomizeBuffer(buf.data(), buf.size());
+ ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(kPageSize));
+
+ // Open the output file as write only.
+ int fd;
+ EXPECT_THAT(fd = memfd_create("negative", 0), SyscallSucceeds());
+ const FileDescriptor out_fd(fd);
+
+ loff_t out_offset = 0xffffffffffffffffull;
+ constexpr int kSize = 2;
+ EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kSize, 0),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+// Write offset + size overflows int64.
+//
+// This is a regression test for b/148041624.
+TEST(SpliceTest, WriteOverflow) {
+ // Create a new pipe.
+ int fds[2];
+ ASSERT_THAT(pipe(fds), SyscallSucceeds());
+ const FileDescriptor rfd(fds[0]);
+ const FileDescriptor wfd(fds[1]);
+
+ // Fill the pipe.
+ std::vector<char> buf(kPageSize);
+ RandomizeBuffer(buf.data(), buf.size());
+ ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
+ SyscallSucceedsWithValue(kPageSize));
+
+ // Open the output file.
+ int fd;
+ EXPECT_THAT(fd = memfd_create("overflow", 0), SyscallSucceeds());
+ const FileDescriptor out_fd(fd);
+
+ // out_offset + kSize overflows INT64_MAX.
+ loff_t out_offset = 0x7ffffffffffffffeull;
+ constexpr int kSize = 3;
+ EXPECT_THAT(splice(rfd.get(), nullptr, out_fd.get(), &out_offset, kSize, 0),
+ SyscallFailsWithErrno(EINVAL));
+}
+
TEST(SpliceTest, SamePipe) {
// Create a new pipe.
int fds[2];
diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc
index 7a99f2636..c951ac3b3 100644
--- a/test/syscalls/linux/stat.cc
+++ b/test/syscalls/linux/stat.cc
@@ -601,8 +601,8 @@ struct kernel_statx {
uint64_t __spare2[14];
};
-int statx(int dirfd, const char *pathname, int flags, unsigned int mask,
- struct kernel_statx *statxbuf) {
+int statx(int dirfd, const char* pathname, int flags, unsigned int mask,
+ struct kernel_statx* statxbuf) {
return syscall(SYS_statx, dirfd, pathname, flags, mask, statxbuf);
}
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index 6b99c021d..525ccbd88 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -814,6 +814,20 @@ TEST_P(TcpSocketTest, FullBuffer) {
t_ = -1;
}
+TEST_P(TcpSocketTest, PollAfterShutdown) {
+ ScopedThread client_thread([this]() {
+ EXPECT_THAT(shutdown(s_, SHUT_WR), SyscallSucceedsWithValue(0));
+ struct pollfd poll_fd = {s_, POLLIN | POLLERR | POLLHUP, 0};
+ EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000),
+ SyscallSucceedsWithValue(1));
+ });
+
+ EXPECT_THAT(shutdown(t_, SHUT_WR), SyscallSucceedsWithValue(0));
+ struct pollfd poll_fd = {t_, POLLIN | POLLERR | POLLHUP, 0};
+ EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10000),
+ SyscallSucceedsWithValue(1));
+}
+
TEST_P(SimpleTcpSocketTest, NonBlockingConnectNoListener) {
// Initialize address to the loopback one.
sockaddr_storage addr =
@@ -1272,6 +1286,59 @@ TEST_P(SimpleTcpSocketTest, SetTCPUserTimeout) {
EXPECT_EQ(get, kTCPUserTimeout);
}
+TEST_P(SimpleTcpSocketTest, SetTCPDeferAcceptNeg) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ // -ve TCP_DEFER_ACCEPT is same as setting it to zero.
+ constexpr int kNeg = -1;
+ EXPECT_THAT(
+ setsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &kNeg, sizeof(kNeg)),
+ SyscallSucceeds());
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, 0);
+}
+
+TEST_P(SimpleTcpSocketTest, GetTCPDeferAcceptDefault) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(s.get(), IPPROTO_TCP, TCP_USER_TIMEOUT, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, 0);
+}
+
+TEST_P(SimpleTcpSocketTest, SetTCPDeferAcceptGreaterThanZero) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+ // kTCPDeferAccept is in seconds.
+ // NOTE: linux translates seconds to # of retries and back from
+ // #of retries to seconds. Which means only certain values
+ // translate back exactly. That's why we use 3 here, a value of
+ // 5 will result in us getting back 7 instead of 5 in the
+ // getsockopt.
+ constexpr int kTCPDeferAccept = 3;
+ ASSERT_THAT(setsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT,
+ &kTCPDeferAccept, sizeof(kTCPDeferAccept)),
+ SyscallSucceeds());
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(s.get(), IPPROTO_TCP, TCP_DEFER_ACCEPT, &get, &get_len),
+ SyscallSucceeds());
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kTCPDeferAccept);
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest,
::testing::Values(AF_INET, AF_INET6));
diff --git a/test/syscalls/linux/udp_socket_errqueue_test_case.cc b/test/syscalls/linux/udp_socket_errqueue_test_case.cc
index 147978f46..fcdba7279 100644
--- a/test/syscalls/linux/udp_socket_errqueue_test_case.cc
+++ b/test/syscalls/linux/udp_socket_errqueue_test_case.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "test/syscalls/linux/udp_socket_test_cases.h"
+#ifndef __fuchsia__
#include <arpa/inet.h>
#include <fcntl.h>
@@ -27,6 +27,7 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/socket_test_util.h"
+#include "test/syscalls/linux/udp_socket_test_cases.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -52,3 +53,5 @@ TEST_P(UdpSocketTest, ErrorQueue) {
} // namespace testing
} // namespace gvisor
+
+#endif // __fuchsia__
diff --git a/test/syscalls/linux/udp_socket_test_cases.cc b/test/syscalls/linux/udp_socket_test_cases.cc
index dc35c2f50..a2f6ef8cc 100644
--- a/test/syscalls/linux/udp_socket_test_cases.cc
+++ b/test/syscalls/linux/udp_socket_test_cases.cc
@@ -1349,8 +1349,9 @@ TEST_P(UdpSocketTest, TimestampIoctlPersistence) {
// outgoing packets, and that a receiving socket with IP_RECVTOS or
// IPV6_RECVTCLASS will create the corresponding control message.
TEST_P(UdpSocketTest, SetAndReceiveTOS) {
- // TODO(b/68320120): IP_RECVTOS/IPV6_RECVTCLASS not supported for netstack.
- SKIP_IF(IsRunningOnGvisor() && !IsRunningWithHostinet());
+ // TODO(b/144868438): IPV6_RECVTCLASS not supported for netstack.
+ SKIP_IF((GetParam() != AddressFamily::kIpv4) && IsRunningOnGvisor() &&
+ !IsRunningWithHostinet());
ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds());
ASSERT_THAT(connect(t_, addr_[0], addrlen_), SyscallSucceeds());
@@ -1421,7 +1422,8 @@ TEST_P(UdpSocketTest, SetAndReceiveTOS) {
// 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/68320120): IP_RECVTOS/IPV6_RECVTCLASS not supported for netstack.
+ // TODO(b/144868438): IPV6_RECVTCLASS not supported for netstack.
+ // TODO(b/146661005): Setting TOS via cmsg not supported for netstack.
SKIP_IF(IsRunningOnGvisor() && !IsRunningWithHostinet());
ASSERT_THAT(bind(s_, addr_[0], addrlen_), SyscallSucceeds());
ASSERT_THAT(connect(t_, addr_[0], addrlen_), SyscallSucceeds());
diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc
index 12b925a51..3a927a430 100644
--- a/test/syscalls/linux/utimes.cc
+++ b/test/syscalls/linux/utimes.cc
@@ -163,12 +163,12 @@ TEST(FutimesatTest, OnRelPath) {
TEST(FutimesatTest, InvalidNsec) {
auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
struct timeval times[4][2] = {{
- {0, 1}, // Valid
+ {0, 1}, // Valid
{1, static_cast<int64_t>(1e7)} // Invalid
},
{
{1, static_cast<int64_t>(1e7)}, // Invalid
- {0, 1} // Valid
+ {0, 1} // Valid
},
{
{0, 1}, // Valid
@@ -288,14 +288,15 @@ TEST(UtimeTest, ZeroAtimeandMtime) {
TEST(UtimensatTest, InvalidNsec) {
auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- struct timespec times[2][2] = {{
- {0, UTIME_OMIT}, // Valid
- {2, static_cast<int64_t>(1e10)} // Invalid
- },
- {
- {2, static_cast<int64_t>(1e10)}, // Invalid
- {0, UTIME_OMIT} // Valid
- }};
+ struct timespec times[2][2] = {
+ {
+ {0, UTIME_OMIT}, // Valid
+ {2, static_cast<int64_t>(1e10)} // Invalid
+ },
+ {
+ {2, static_cast<int64_t>(1e10)}, // Invalid
+ {0, UTIME_OMIT} // Valid
+ }};
for (unsigned int i = 0; i < sizeof(times) / sizeof(times[0]); i++) {
std::cout << "test:" << i << "\n";
diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc
index 75740238c..ab21d68c6 100644
--- a/test/syscalls/linux/xattr.cc
+++ b/test/syscalls/linux/xattr.cc
@@ -26,6 +26,7 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/file_base.h"
#include "test/util/capability_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"
@@ -59,7 +60,8 @@ TEST_F(XattrTest, XattrLargeName) {
std::string name = "user.";
name += std::string(XATTR_NAME_MAX - name.length(), 'a');
- // TODO(b/127675828): Support setxattr and getxattr.
+ // An xattr should be whitelisted before it can be accessed--do not allow
+ // arbitrary xattrs to be read/written in gVisor.
if (!IsRunningOnGvisor()) {
EXPECT_THAT(setxattr(path, name.c_str(), nullptr, 0, /*flags=*/0),
SyscallSucceeds());
@@ -83,59 +85,53 @@ TEST_F(XattrTest, XattrInvalidPrefix) {
SyscallFailsWithErrno(EOPNOTSUPP));
}
-TEST_F(XattrTest, XattrReadOnly) {
+// 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();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
size_t size = sizeof(val);
- // TODO(b/127675828): Support setxattr and getxattr.
- if (!IsRunningOnGvisor()) {
- EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0),
- SyscallSucceeds());
- }
+ 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));
- // TODO(b/127675828): Support setxattr and getxattr.
- if (!IsRunningOnGvisor()) {
- char buf = '-';
- EXPECT_THAT(getxattr(path, name, &buf, size),
- SyscallSucceedsWithValue(size));
- EXPECT_EQ(buf, val);
- }
+ char buf = '-';
+ EXPECT_THAT(getxattr(path, name, &buf, size), SyscallSucceedsWithValue(size));
+ EXPECT_EQ(buf, val);
}
-TEST_F(XattrTest, XattrWriteOnly) {
+// 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();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
size_t size = sizeof(val);
- // TODO(b/127675828): Support setxattr and getxattr.
- if (!IsRunningOnGvisor()) {
- EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0),
- SyscallSucceeds());
- }
+ EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds());
EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(EACCES));
}
TEST_F(XattrTest, XattrTrustedWithNonadmin) {
- // TODO(b/127675828): Support setxattr and getxattr.
+ // TODO(b/148380782): Support setxattr and getxattr with "trusted" prefix.
SKIP_IF(IsRunningOnGvisor());
SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
@@ -147,11 +143,8 @@ TEST_F(XattrTest, XattrTrustedWithNonadmin) {
}
TEST_F(XattrTest, XattrOnDirectory) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- char name[] = "user.abc";
+ const char name[] = "user.test";
EXPECT_THAT(setxattr(dir.path().c_str(), name, NULL, 0, /*flags=*/0),
SyscallSucceeds());
EXPECT_THAT(getxattr(dir.path().c_str(), name, NULL, 0),
@@ -159,13 +152,10 @@ TEST_F(XattrTest, XattrOnDirectory) {
}
TEST_F(XattrTest, XattrOnSymlink) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
TempPath link = ASSERT_NO_ERRNO_AND_VALUE(
TempPath::CreateSymlinkTo(dir.path(), test_file_name_));
- char name[] = "user.abc";
+ const char name[] = "user.test";
EXPECT_THAT(setxattr(link.path().c_str(), name, NULL, 0, /*flags=*/0),
SyscallSucceeds());
EXPECT_THAT(getxattr(link.path().c_str(), name, NULL, 0),
@@ -173,7 +163,7 @@ TEST_F(XattrTest, XattrOnSymlink) {
}
TEST_F(XattrTest, XattrOnInvalidFileTypes) {
- char name[] = "user.abc";
+ const char name[] = "user.test";
char char_device[] = "/dev/zero";
EXPECT_THAT(setxattr(char_device, name, NULL, 0, /*flags=*/0),
@@ -191,11 +181,8 @@ TEST_F(XattrTest, XattrOnInvalidFileTypes) {
}
TEST_F(XattrTest, SetxattrSizeSmallerThanValue) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ 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),
@@ -209,11 +196,8 @@ TEST_F(XattrTest, SetxattrSizeSmallerThanValue) {
}
TEST_F(XattrTest, SetxattrZeroSize) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
EXPECT_THAT(setxattr(path, name, &val, 0, /*flags=*/0), SyscallSucceeds());
@@ -225,7 +209,7 @@ TEST_F(XattrTest, SetxattrZeroSize) {
TEST_F(XattrTest, SetxattrSizeTooLarge) {
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ 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
@@ -235,43 +219,29 @@ TEST_F(XattrTest, SetxattrSizeTooLarge) {
EXPECT_THAT(setxattr(path, name, val.data(), size, /*flags=*/0),
SyscallFailsWithErrno(E2BIG));
- // TODO(b/127675828): Support setxattr and getxattr.
- if (!IsRunningOnGvisor()) {
- EXPECT_THAT(getxattr(path, name, nullptr, 0),
- SyscallFailsWithErrno(ENODATA));
- }
+ EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA));
}
TEST_F(XattrTest, SetxattrNullValueAndNonzeroSize) {
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
EXPECT_THAT(setxattr(path, name, nullptr, 1, /*flags=*/0),
SyscallFailsWithErrno(EFAULT));
- // TODO(b/127675828): Support setxattr and getxattr.
- if (!IsRunningOnGvisor()) {
- EXPECT_THAT(getxattr(path, name, nullptr, 0),
- SyscallFailsWithErrno(ENODATA));
- }
+ EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA));
}
TEST_F(XattrTest, SetxattrNullValueAndZeroSize) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ 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) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
std::vector<char> val(XATTR_SIZE_MAX + 1);
std::fill(val.begin(), val.end(), 'a');
size_t size = 1;
@@ -286,11 +256,8 @@ TEST_F(XattrTest, SetxattrValueTooLargeButOKSize) {
}
TEST_F(XattrTest, SetxattrReplaceWithSmaller) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
std::vector<char> val = {'a', 'a'};
EXPECT_THAT(setxattr(path, name, val.data(), 2, /*flags=*/0),
SyscallSucceeds());
@@ -304,11 +271,8 @@ TEST_F(XattrTest, SetxattrReplaceWithSmaller) {
}
TEST_F(XattrTest, SetxattrReplaceWithLarger) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
std::vector<char> val = {'a', 'a'};
EXPECT_THAT(setxattr(path, name, val.data(), 1, /*flags=*/0),
SyscallSucceeds());
@@ -321,11 +285,8 @@ TEST_F(XattrTest, SetxattrReplaceWithLarger) {
}
TEST_F(XattrTest, SetxattrCreateFlag) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_CREATE),
SyscallSucceeds());
EXPECT_THAT(setxattr(path, name, nullptr, 0, XATTR_CREATE),
@@ -335,11 +296,8 @@ TEST_F(XattrTest, SetxattrCreateFlag) {
}
TEST_F(XattrTest, SetxattrReplaceFlag) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ 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());
@@ -357,11 +315,8 @@ TEST_F(XattrTest, SetxattrInvalidFlags) {
}
TEST_F(XattrTest, Getxattr) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
int val = 1234;
size_t size = sizeof(val);
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds());
@@ -372,11 +327,8 @@ TEST_F(XattrTest, Getxattr) {
}
TEST_F(XattrTest, GetxattrSizeSmallerThanValue) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ 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());
@@ -387,11 +339,8 @@ TEST_F(XattrTest, GetxattrSizeSmallerThanValue) {
}
TEST_F(XattrTest, GetxattrSizeLargerThanValue) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
EXPECT_THAT(setxattr(path, name, &val, 1, /*flags=*/0), SyscallSucceeds());
@@ -405,11 +354,8 @@ TEST_F(XattrTest, GetxattrSizeLargerThanValue) {
}
TEST_F(XattrTest, GetxattrZeroSize) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
EXPECT_THAT(setxattr(path, name, &val, sizeof(val), /*flags=*/0),
SyscallSucceeds());
@@ -421,11 +367,8 @@ TEST_F(XattrTest, GetxattrZeroSize) {
}
TEST_F(XattrTest, GetxattrSizeTooLarge) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
EXPECT_THAT(setxattr(path, name, &val, sizeof(val), /*flags=*/0),
SyscallSucceeds());
@@ -440,11 +383,8 @@ TEST_F(XattrTest, GetxattrSizeTooLarge) {
}
TEST_F(XattrTest, GetxattrNullValue) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
size_t size = sizeof(val);
EXPECT_THAT(setxattr(path, name, &val, size, /*flags=*/0), SyscallSucceeds());
@@ -454,11 +394,8 @@ TEST_F(XattrTest, GetxattrNullValue) {
}
TEST_F(XattrTest, GetxattrNullValueAndZeroSize) {
- // TODO(b/127675828): Support setxattr and getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- char name[] = "user.abc";
+ const char name[] = "user.test";
char val = 'a';
size_t size = sizeof(val);
// Set value with zero size.
@@ -473,15 +410,51 @@ TEST_F(XattrTest, GetxattrNullValueAndZeroSize) {
}
TEST_F(XattrTest, GetxattrNonexistentName) {
- // TODO(b/127675828): Support getxattr.
- SKIP_IF(IsRunningOnGvisor());
-
const char* path = test_file_name_.c_str();
- std::string name = "user.nonexistent";
- EXPECT_THAT(getxattr(path, name.c_str(), nullptr, 0),
+ const char name[] = "user.test";
+ EXPECT_THAT(getxattr(path, name, nullptr, 0), SyscallFailsWithErrno(ENODATA));
+}
+
+TEST_F(XattrTest, LGetSetxattrOnSymlink) {
+ 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(), nullptr, nullptr, 0, 0),
+ SyscallFailsWithErrno(EPERM));
+ EXPECT_THAT(lgetxattr(link.path().c_str(), nullptr, nullptr, 0),
SyscallFailsWithErrno(ENODATA));
}
+TEST_F(XattrTest, LGetSetxattrOnNonsymlink) {
+ 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);
+}
+
+TEST_F(XattrTest, FGetSetxattr) {
+ 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);
+}
+
} // namespace
} // namespace testing
diff --git a/test/uds/BUILD b/test/uds/BUILD
index a3843e699..51e2c7ce8 100644
--- a/test/uds/BUILD
+++ b/test/uds/BUILD
@@ -1,4 +1,4 @@
-load("@io_bazel_rules_go//go:def.bzl", "go_library")
+load("//tools:defs.bzl", "go_library")
package(
default_visibility = ["//:sandbox"],
@@ -9,7 +9,6 @@ go_library(
name = "uds",
testonly = 1,
srcs = ["uds.go"],
- importpath = "gvisor.dev/gvisor/test/uds",
deps = [
"//pkg/log",
"//pkg/unet",
diff --git a/test/util/BUILD b/test/util/BUILD
index cbc728159..1ac8b3fd6 100644
--- a/test/util/BUILD
+++ b/test/util/BUILD
@@ -1,5 +1,4 @@
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load("//test/syscalls:build_defs.bzl", "select_for_linux")
+load("//tools:defs.bzl", "cc_library", "cc_test", "select_system")
package(
default_visibility = ["//:sandbox"],
@@ -142,12 +141,13 @@ cc_library(
cc_library(
name = "save_util",
testonly = 1,
- srcs = ["save_util.cc"] +
- select_for_linux(
- ["save_util_linux.cc"],
- ["save_util_other.cc"],
- ),
+ srcs = [
+ "save_util.cc",
+ "save_util_linux.cc",
+ "save_util_other.cc",
+ ],
hdrs = ["save_util.h"],
+ defines = select_system(),
)
cc_library(
@@ -166,6 +166,14 @@ cc_library(
)
cc_library(
+ name = "platform_util",
+ testonly = 1,
+ srcs = ["platform_util.cc"],
+ hdrs = ["platform_util.h"],
+ deps = [":test_util"],
+)
+
+cc_library(
name = "posix_error",
testonly = 1,
srcs = ["posix_error.cc"],
@@ -234,13 +242,11 @@ cc_library(
testonly = 1,
srcs = [
"test_util.cc",
- ] + select_for_linux(
- [
- "test_util_impl.cc",
- "test_util_runfiles.cc",
- ],
- ),
+ "test_util_impl.cc",
+ "test_util_runfiles.cc",
+ ],
hdrs = ["test_util.h"],
+ defines = select_system(),
deps = [
":fs_util",
":logging",
diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc
index 5d733887b..9fee52fbb 100644
--- a/test/util/capability_util.cc
+++ b/test/util/capability_util.cc
@@ -36,10 +36,10 @@ PosixErrorOr<bool> CanCreateUserNamespace() {
ASSIGN_OR_RETURN_ERRNO(
auto child_stack,
MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE));
- int const child_pid =
- clone(+[](void*) { return 0; },
- reinterpret_cast<void*>(child_stack.addr() + kPageSize),
- CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr);
+ int const child_pid = clone(
+ +[](void*) { return 0; },
+ reinterpret_cast<void*>(child_stack.addr() + kPageSize),
+ CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr);
if (child_pid > 0) {
int status;
int const ret = waitpid(child_pid, &status, /* options = */ 0);
diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc
index 042cec94a..052781445 100644
--- a/test/util/fs_util.cc
+++ b/test/util/fs_util.cc
@@ -452,7 +452,7 @@ PosixErrorOr<std::string> MakeAbsolute(absl::string_view filename,
std::string CleanPath(const absl::string_view unclean_path) {
std::string path = std::string(unclean_path);
- const char *src = path.c_str();
+ const char* src = path.c_str();
std::string::iterator dst = path.begin();
// Check for absolute path and determine initial backtrack limit.
diff --git a/test/util/fs_util.h b/test/util/fs_util.h
index ee1b341d7..caf19b24d 100644
--- a/test/util/fs_util.h
+++ b/test/util/fs_util.h
@@ -26,6 +26,17 @@
namespace gvisor {
namespace testing {
+
+// O_LARGEFILE as defined by Linux. glibc tries to be clever by setting it to 0
+// because "it isn't needed", even though Linux can return it via F_GETFL.
+#if defined(__x86_64__)
+constexpr int kOLargeFile = 00100000;
+#elif defined(__aarch64__)
+constexpr int kOLargeFile = 00400000;
+#else
+#error "Unknown architecture"
+#endif
+
// Returns a status or the current working directory.
PosixErrorOr<std::string> GetCWD();
diff --git a/test/util/mount_util.h b/test/util/mount_util.h
index 484de560e..09e2281eb 100644
--- a/test/util/mount_util.h
+++ b/test/util/mount_util.h
@@ -31,10 +31,10 @@ namespace testing {
// Mount mounts the filesystem, and unmounts when the returned reference is
// destroyed.
-inline PosixErrorOr<Cleanup> Mount(const std::string &source,
- const std::string &target,
- const std::string &fstype, uint64_t mountflags,
- const std::string &data,
+inline PosixErrorOr<Cleanup> Mount(const std::string& source,
+ const std::string& target,
+ const std::string& fstype,
+ uint64_t mountflags, const std::string& data,
uint64_t umountflags) {
if (mount(source.c_str(), target.c_str(), fstype.c_str(), mountflags,
data.c_str()) == -1) {
diff --git a/test/util/multiprocess_util.h b/test/util/multiprocess_util.h
index 61526b4e7..2f3bf4a6f 100644
--- a/test/util/multiprocess_util.h
+++ b/test/util/multiprocess_util.h
@@ -99,11 +99,13 @@ inline PosixErrorOr<Cleanup> ForkAndExec(const std::string& filename,
const ExecveArray& argv,
const ExecveArray& envv, pid_t* child,
int* execve_errno) {
- return ForkAndExec(filename, argv, envv, [] {}, child, execve_errno);
+ return ForkAndExec(
+ filename, argv, envv, [] {}, child, execve_errno);
}
// Equivalent to ForkAndExec, except using dirfd and flags with execveat.
-PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd, const std::string& pathname,
+PosixErrorOr<Cleanup> ForkAndExecveat(int32_t dirfd,
+ const std::string& pathname,
const ExecveArray& argv,
const ExecveArray& envv, int flags,
const std::function<void()>& fn,
diff --git a/test/util/platform_util.cc b/test/util/platform_util.cc
new file mode 100644
index 000000000..2724e63f3
--- /dev/null
+++ b/test/util/platform_util.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/util/platform_util.h"
+
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+PlatformSupport PlatformSupport32Bit() {
+ if (GvisorPlatform() == Platform::kPtrace) {
+ return PlatformSupport::NotSupported;
+ } else if (GvisorPlatform() == Platform::kKVM) {
+ return PlatformSupport::Segfault;
+ } else {
+ return PlatformSupport::Allowed;
+ }
+}
+
+PlatformSupport PlatformSupportAlignmentCheck() {
+ return PlatformSupport::Allowed;
+}
+
+PlatformSupport PlatformSupportMultiProcess() {
+ return PlatformSupport::Allowed;
+}
+
+PlatformSupport PlatformSupportInt3() {
+ if (GvisorPlatform() == Platform::kKVM) {
+ return PlatformSupport::NotSupported;
+ } else {
+ return PlatformSupport::Allowed;
+ }
+}
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/util/platform_util.h b/test/util/platform_util.h
new file mode 100644
index 000000000..28cc92371
--- /dev/null
+++ b/test/util/platform_util.h
@@ -0,0 +1,56 @@
+// Copyright 2020 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef GVISOR_TEST_UTIL_PLATFORM_UTIL_H_
+#define GVISOR_TEST_UTIL_PLATFORM_UTIL_H_
+
+namespace gvisor {
+namespace testing {
+
+// PlatformSupport is a generic enumeration of classes of support.
+//
+// It is up to the individual functions and callers to agree on the precise
+// definition for each case. The document here generally refers to 32-bit
+// as an example. Many cases will use only NotSupported and Allowed.
+enum class PlatformSupport {
+ // The feature is not supported on the current platform.
+ //
+ // In the case of 32-bit, this means that calls will generally be interpreted
+ // as 64-bit calls, and there is no support for 32-bit binaries, long calls,
+ // etc. This usually means that the underlying implementation just pretends
+ // that 32-bit doesn't exist.
+ NotSupported,
+
+ // Calls will be ignored by the kernel with a fixed error.
+ Ignored,
+
+ // Calls will result in a SIGSEGV or similar fault.
+ Segfault,
+
+ // The feature is supported as expected.
+ //
+ // In the case of 32-bit, this means that the system call or far call will be
+ // handled properly.
+ Allowed,
+};
+
+PlatformSupport PlatformSupport32Bit();
+PlatformSupport PlatformSupportAlignmentCheck();
+PlatformSupport PlatformSupportMultiProcess();
+PlatformSupport PlatformSupportInt3();
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_UTIL_PLATFORM_UTL_H_
diff --git a/test/util/save_util_linux.cc b/test/util/save_util_linux.cc
index 7a0f14342..d0aea8e6a 100644
--- a/test/util/save_util_linux.cc
+++ b/test/util/save_util_linux.cc
@@ -12,22 +12,38 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifdef __linux__
+
#include <errno.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "test/util/save_util.h"
+#if defined(__x86_64__) || defined(__i386__)
+#define SYS_TRIGGER_SAVE SYS_create_module
+#elif defined(__aarch64__)
+#define SYS_TRIGGER_SAVE SYS_finit_module
+#else
+#error "Unknown architecture"
+#endif
+
namespace gvisor {
namespace testing {
void MaybeSave() {
if (internal::ShouldSave()) {
int orig_errno = errno;
- syscall(SYS_create_module, nullptr, 0);
+ // We use it to trigger saving the sentry state
+ // when this syscall is called.
+ // Notice: this needs to be a valid syscall
+ // that is not used in any of the syscall tests.
+ syscall(SYS_TRIGGER_SAVE, nullptr, 0);
errno = orig_errno;
}
}
} // namespace testing
} // namespace gvisor
+
+#endif
diff --git a/test/util/save_util_other.cc b/test/util/save_util_other.cc
index 1aca663b7..931af2c29 100644
--- a/test/util/save_util_other.cc
+++ b/test/util/save_util_other.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifndef __linux__
+
namespace gvisor {
namespace testing {
@@ -21,3 +23,5 @@ void MaybeSave() {
} // namespace testing
} // namespace gvisor
+
+#endif
diff --git a/test/util/test_util.cc b/test/util/test_util.cc
index a4f78eec2..95e1e0c96 100644
--- a/test/util/test_util.cc
+++ b/test/util/test_util.cc
@@ -45,20 +45,13 @@ namespace testing {
bool IsRunningOnGvisor() { return GvisorPlatform() != Platform::kNative; }
-Platform GvisorPlatform() {
+const std::string GvisorPlatform() {
// Set by runner.go.
char* env = getenv(TEST_ON_GVISOR);
if (!env) {
return Platform::kNative;
}
- if (strcmp(env, "ptrace") == 0) {
- return Platform::kPtrace;
- }
- if (strcmp(env, "kvm") == 0) {
- return Platform::kKVM;
- }
- std::cerr << "unknown platform " << env;
- abort();
+ return std::string(env);
}
bool IsRunningWithHostinet() {
diff --git a/test/util/test_util.h b/test/util/test_util.h
index b3235c7e3..2d22b0eb8 100644
--- a/test/util/test_util.h
+++ b/test/util/test_util.h
@@ -26,16 +26,13 @@
// IsRunningOnGvisor returns true if the test is known to be running on gVisor.
// GvisorPlatform can be used to get more detail:
//
-// switch (GvisorPlatform()) {
-// case Platform::kNative:
-// case Platform::kGvisor:
-// EXPECT_THAT(mmap(...), SyscallSucceeds());
-// break;
-// case Platform::kPtrace:
-// EXPECT_THAT(mmap(...), SyscallFailsWithErrno(ENOSYS));
-// break;
+// if (GvisorPlatform() == Platform::kPtrace) {
+// ...
// }
//
+// SetupGvisorDeathTest ensures that signal handling does not interfere with
+/// tests that rely on fatal signals.
+//
// Matchers
// ========
//
@@ -213,13 +210,15 @@ void TestInit(int* argc, char*** argv);
if (expr) GTEST_SKIP() << #expr; \
} while (0)
-enum class Platform {
- kNative,
- kKVM,
- kPtrace,
-};
+// Platform contains platform names.
+namespace Platform {
+constexpr char kNative[] = "native";
+constexpr char kPtrace[] = "ptrace";
+constexpr char kKVM[] = "kvm";
+} // namespace Platform
+
bool IsRunningOnGvisor();
-Platform GvisorPlatform();
+const std::string GvisorPlatform();
bool IsRunningWithHostinet();
#ifdef __linux__
diff --git a/test/util/test_util_runfiles.cc b/test/util/test_util_runfiles.cc
index 7210094eb..694d21692 100644
--- a/test/util/test_util_runfiles.cc
+++ b/test/util/test_util_runfiles.cc
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifndef __fuchsia__
+
#include <iostream>
#include <string>
@@ -44,3 +46,5 @@ std::string RunfilePath(std::string path) {
} // namespace testing
} // namespace gvisor
+
+#endif // __fuchsia__