summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/benchmarks/base/startup_test.go12
-rw-r--r--test/benchmarks/tcp/tcp_proxy.go8
-rw-r--r--test/benchmarks/tools/fio_test.go2
-rw-r--r--test/e2e/integration_test.go192
-rw-r--r--test/fsstress/BUILD6
-rw-r--r--test/fsstress/fsstress_test.go53
-rw-r--r--test/packetimpact/README.md8
-rw-r--r--test/packetimpact/dut/posix_server.cc2
-rw-r--r--test/packetimpact/testbench/dut.go83
-rw-r--r--test/packetimpact/testbench/dut_client.go3
-rw-r--r--test/packetimpact/testbench/testbench.go3
-rw-r--r--test/packetimpact/tests/generic_dgram_socket_send_recv_test.go9
-rw-r--r--test/packetimpact/tests/tcp_connect_icmp_error_test.go35
-rw-r--r--test/packetimpact/tests/tcp_info_test.go31
-rw-r--r--test/packetimpact/tests/tcp_linger_test.go82
-rw-r--r--test/packetimpact/tests/tcp_network_unreachable_test.go8
-rw-r--r--test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go96
-rw-r--r--test/packetimpact/tests/udp_icmp_error_propagation_test.go21
-rw-r--r--test/perf/BUILD42
-rw-r--r--test/perf/linux/BUILD114
-rw-r--r--test/perf/linux/dup_benchmark.cc55
-rw-r--r--test/perf/linux/randread_benchmark.cc2
-rw-r--r--test/perf/linux/send_recv_benchmark.cc23
-rw-r--r--test/perf/linux/verity_open_benchmark.cc71
-rw-r--r--test/perf/linux/verity_open_read_close_benchmark.cc75
-rw-r--r--test/perf/linux/verity_randread_benchmark.cc108
-rw-r--r--test/perf/linux/verity_read_benchmark.cc69
-rw-r--r--test/perf/linux/verity_stat_benchmark.cc84
-rw-r--r--test/root/chroot_test.go10
-rw-r--r--test/runner/BUILD1
-rw-r--r--test/runner/defs.bzl9
-rw-r--r--test/runner/gtest/gtest.go21
-rw-r--r--test/runner/main.go51
-rw-r--r--test/runner/setup_container/BUILD19
-rw-r--r--test/runner/setup_container/setup_container.cc79
-rw-r--r--test/runtimes/runner/lib/lib.go2
-rw-r--r--test/syscalls/BUILD25
-rw-r--r--test/syscalls/linux/BUILD369
-rw-r--r--test/syscalls/linux/accept_bind.cc17
-rw-r--r--test/syscalls/linux/accept_bind_stream.cc2
-rw-r--r--test/syscalls/linux/bind.cc2
-rw-r--r--test/syscalls/linux/cgroup.cc17
-rw-r--r--test/syscalls/linux/chroot.cc226
-rw-r--r--test/syscalls/linux/connect_external.cc2
-rw-r--r--test/syscalls/linux/dup.cc41
-rw-r--r--test/syscalls/linux/eventfd.cc25
-rw-r--r--test/syscalls/linux/exec.cc3
-rw-r--r--test/syscalls/linux/fallocate.cc2
-rw-r--r--test/syscalls/linux/fcntl.cc2
-rw-r--r--test/syscalls/linux/flock.cc2
-rw-r--r--test/syscalls/linux/inotify.cc28
-rw-r--r--test/syscalls/linux/ioctl.cc2
-rw-r--r--test/syscalls/linux/ip6tables.cc2
-rw-r--r--test/syscalls/linux/ip_socket_test_util.cc8
-rw-r--r--test/syscalls/linux/ip_socket_test_util.h9
-rw-r--r--test/syscalls/linux/itimer.cc7
-rw-r--r--test/syscalls/linux/link.cc3
-rw-r--r--test/syscalls/linux/lseek.cc3
-rw-r--r--test/syscalls/linux/memfd.cc5
-rw-r--r--test/syscalls/linux/mmap.cc102
-rw-r--r--test/syscalls/linux/msgqueue.cc885
-rw-r--r--test/syscalls/linux/network_namespace.cc19
-rw-r--r--test/syscalls/linux/packet_socket.cc671
-rw-r--r--test/syscalls/linux/packet_socket_dgram.cc550
-rw-r--r--test/syscalls/linux/packet_socket_raw.cc59
-rw-r--r--test/syscalls/linux/partial_bad_buffer.cc2
-rw-r--r--test/syscalls/linux/ping_socket.cc18
-rw-r--r--test/syscalls/linux/pipe.cc24
-rw-r--r--test/syscalls/linux/poll.cc4
-rw-r--r--test/syscalls/linux/prctl.cc15
-rw-r--r--test/syscalls/linux/priority.cc23
-rw-r--r--test/syscalls/linux/proc.cc42
-rw-r--r--test/syscalls/linux/proc_net.cc88
-rw-r--r--test/syscalls/linux/ptrace.cc458
-rw-r--r--test/syscalls/linux/pty.cc88
-rw-r--r--test/syscalls/linux/raw_socket.cc303
-rw-r--r--test/syscalls/linux/raw_socket_hdrincl.cc28
-rw-r--r--test/syscalls/linux/raw_socket_icmp.cc33
-rw-r--r--test/syscalls/linux/readahead.cc2
-rw-r--r--test/syscalls/linux/semaphore.cc11
-rw-r--r--test/syscalls/linux/sendfile.cc100
-rw-r--r--test/syscalls/linux/sendfile_socket.cc2
-rw-r--r--test/syscalls/linux/socket.cc7
-rw-r--r--test/syscalls/linux/socket_abstract.cc2
-rw-r--r--test/syscalls/linux/socket_bind_to_device.cc2
-rw-r--r--test/syscalls/linux/socket_bind_to_device_distribution.cc30
-rw-r--r--test/syscalls/linux/socket_bind_to_device_sequence.cc2
-rw-r--r--test/syscalls/linux/socket_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_blocking.h2
-rw-r--r--test/syscalls/linux/socket_capability.cc2
-rw-r--r--test/syscalls/linux/socket_filesystem.cc2
-rw-r--r--test/syscalls/linux/socket_generic.h2
-rw-r--r--test/syscalls/linux/socket_generic_stress.cc50
-rw-r--r--test/syscalls/linux/socket_generic_test_cases.cc46
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc771
-rw-r--r--test/syscalls/linux/socket_inet_loopback_isolated.cc489
-rw-r--r--test/syscalls/linux/socket_inet_loopback_nogotsan.cc172
-rw-r--r--test/syscalls/linux/socket_inet_loopback_test_params.h86
-rw-r--r--test/syscalls/linux/socket_ip_loopback_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.cc2
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.h2
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic_loopback.cc2
-rw-r--r--test/syscalls/linux/socket_ip_tcp_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_tcp_udp_generic.cc2
-rw-r--r--test/syscalls/linux/socket_ip_udp_generic.cc2
-rw-r--r--test/syscalls/linux/socket_ip_udp_generic.h2
-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_udp_unbound_external_networking.cc2
-rw-r--r--test/syscalls/linux/socket_ip_udp_unbound_external_networking.h2
-rw-r--r--test/syscalls/linux/socket_ip_unbound.cc2
-rw-r--r--test/syscalls/linux/socket_ip_unbound_netlink.cc2
-rw-r--r--test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc299
-rw-r--r--test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h31
-rw-r--r--test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc28
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.cc325
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.h2
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc2
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc4
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc2
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc6
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound.cc2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound.h2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc2
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h2
-rw-r--r--test/syscalls/linux/socket_netdevice.cc7
-rw-r--r--test/syscalls/linux/socket_netlink.cc2
-rw-r--r--test/syscalls/linux/socket_netlink_route.cc118
-rw-r--r--test/syscalls/linux/socket_netlink_uevent.cc2
-rw-r--r--test/syscalls/linux/socket_netlink_util.cc2
-rw-r--r--test/syscalls/linux/socket_non_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_non_blocking.h2
-rw-r--r--test/syscalls/linux/socket_non_stream.cc2
-rw-r--r--test/syscalls/linux/socket_non_stream.h2
-rw-r--r--test/syscalls/linux/socket_non_stream_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_non_stream_blocking.h2
-rw-r--r--test/syscalls/linux/socket_stream.cc2
-rw-r--r--test/syscalls/linux/socket_stream.h2
-rw-r--r--test/syscalls/linux/socket_stream_blocking.cc2
-rw-r--r--test/syscalls/linux/socket_stream_blocking.h2
-rw-r--r--test/syscalls/linux/socket_stream_nonblock.cc2
-rw-r--r--test/syscalls/linux/socket_stream_nonblock.h2
-rw-r--r--test/syscalls/linux/socket_unix.cc17
-rw-r--r--test/syscalls/linux/socket_unix.h2
-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_cmsg.cc2
-rw-r--r--test/syscalls/linux/socket_unix_cmsg.h2
-rw-r--r--test/syscalls/linux/socket_unix_dgram.cc2
-rw-r--r--test/syscalls/linux/socket_unix_dgram.h2
-rw-r--r--test/syscalls/linux/socket_unix_dgram_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_dgram_non_blocking.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.cc2
-rw-r--r--test/syscalls/linux/socket_unix_non_stream.h2
-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.cc2
-rw-r--r--test/syscalls/linux/socket_unix_seqpacket.h2
-rw-r--r--test/syscalls/linux/socket_unix_seqpacket_local.cc2
-rw-r--r--test/syscalls/linux/socket_unix_stream.cc17
-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/socket_unix_unbound_abstract.cc2
-rw-r--r--test/syscalls/linux/socket_unix_unbound_dgram.cc2
-rw-r--r--test/syscalls/linux/socket_unix_unbound_filesystem.cc2
-rw-r--r--test/syscalls/linux/socket_unix_unbound_seqpacket.cc2
-rw-r--r--test/syscalls/linux/socket_unix_unbound_stream.cc2
-rw-r--r--test/syscalls/linux/splice.cc103
-rw-r--r--test/syscalls/linux/stat.cc2
-rw-r--r--test/syscalls/linux/statfs.cc2
-rw-r--r--test/syscalls/linux/tcp_socket.cc118
-rw-r--r--test/syscalls/linux/tuntap.cc240
-rw-r--r--test/syscalls/linux/udp_bind.cc2
-rw-r--r--test/syscalls/linux/udp_socket.cc215
-rw-r--r--test/syscalls/linux/uidgid.cc4
-rw-r--r--test/syscalls/linux/uname.cc3
-rw-r--r--test/syscalls/linux/unix_domain_socket_test_util.h2
-rw-r--r--test/syscalls/linux/verity_getdents.cc12
-rw-r--r--test/syscalls/linux/verity_ioctl.cc44
-rw-r--r--test/syscalls/linux/verity_mmap.cc16
-rw-r--r--test/syscalls/linux/verity_symlink.cc117
-rw-r--r--test/util/BUILD51
-rw-r--r--test/util/capability_util.h109
-rw-r--r--test/util/cgroup_util.cc18
-rw-r--r--test/util/cgroup_util.h12
-rw-r--r--test/util/fs_util.cc3
-rw-r--r--test/util/fuchsia_capability_util.cc73
-rw-r--r--test/util/linux_capability_util.cc (renamed from test/util/capability_util.cc)14
-rw-r--r--test/util/linux_capability_util.h128
-rw-r--r--test/util/posix_error.h10
-rw-r--r--test/util/socket_util.cc (renamed from test/syscalls/linux/socket_test_util.cc)185
-rw-r--r--test/util/socket_util.h (renamed from test/syscalls/linux/socket_test_util.h)16
-rw-r--r--test/util/socket_util_impl.cc (renamed from test/syscalls/linux/socket_test_util_impl.cc)2
-rw-r--r--test/util/test_util_impl.cc13
-rw-r--r--test/util/verity_util.cc20
-rw-r--r--test/util/verity_util.h11
207 files changed, 6791 insertions, 3231 deletions
diff --git a/test/benchmarks/base/startup_test.go b/test/benchmarks/base/startup_test.go
index 05a43ad17..197241622 100644
--- a/test/benchmarks/base/startup_test.go
+++ b/test/benchmarks/base/startup_test.go
@@ -34,15 +34,19 @@ func BenchmarkStartupEmpty(b *testing.B) {
defer machine.CleanUp()
ctx := context.Background()
+ b.StopTimer()
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
harness.DebugLog(b, "Running container: %d", i)
container := machine.GetContainer(ctx, b)
- defer container.CleanUp(ctx)
- if _, err := container.Run(ctx, dockerutil.RunOpts{
+ b.StartTimer()
+ if err := container.Spawn(ctx, dockerutil.RunOpts{
Image: "benchmarks/alpine",
- }, "true"); err != nil {
- b.Fatalf("failed to run container: %v", err)
+ }, "sleep", "100"); err != nil {
+ b.Fatalf("failed to start container: %v", err)
}
+ b.StopTimer()
+ container.CleanUp(ctx)
harness.DebugLog(b, "Ran container: %d", i)
}
}
diff --git a/test/benchmarks/tcp/tcp_proxy.go b/test/benchmarks/tcp/tcp_proxy.go
index be74e4d4a..bc0d8fd38 100644
--- a/test/benchmarks/tcp/tcp_proxy.go
+++ b/test/benchmarks/tcp/tcp_proxy.go
@@ -208,8 +208,12 @@ func newNetstackImpl(mode string) (impl, error) {
if err := s.CreateNIC(nicID, fifo.New(ep, runtime.GOMAXPROCS(0), 1000)); err != nil {
return nil, fmt.Errorf("error creating NIC %q: %v", *iface, err)
}
- if err := s.AddAddress(nicID, ipv4.ProtocolNumber, parsedAddr); err != nil {
- return nil, fmt.Errorf("error adding IP address to %q: %v", *iface, err)
+ protocolAddr := tcpip.ProtocolAddress{
+ Protocol: ipv4.ProtocolNumber,
+ AddressWithPrefix: parsedAddr.WithPrefix(),
+ }
+ if err := s.AddProtocolAddress(nicID, protocolAddr, stack.AddressProperties{}); err != nil {
+ return nil, fmt.Errorf("error adding IP address %+v to %q: %w", protocolAddr, *iface, err)
}
subnet, err := tcpip.NewSubnet(parsedDest, parsedMask)
diff --git a/test/benchmarks/tools/fio_test.go b/test/benchmarks/tools/fio_test.go
index a98277150..3b1f852ce 100644
--- a/test/benchmarks/tools/fio_test.go
+++ b/test/benchmarks/tools/fio_test.go
@@ -86,7 +86,7 @@ func TestFio(t *testing.T) {
fio := Fio{}
// WriteBandwidth.
got, err := fio.parseBandwidth(sampleData, false)
- var want float64 = 1753471.0 * 1024
+ want := 1753471.0 * 1024
if err != nil {
t.Fatalf("parse failed with err: %v", err)
} else if got != want {
diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go
index 1accc3b3b..d41139944 100644
--- a/test/e2e/integration_test.go
+++ b/test/e2e/integration_test.go
@@ -30,6 +30,7 @@ import (
"net/http"
"os"
"path/filepath"
+ "regexp"
"strconv"
"strings"
"testing"
@@ -43,6 +44,12 @@ import (
// defaultWait is the default wait time used for tests.
const defaultWait = time.Minute
+func TestMain(m *testing.M) {
+ dockerutil.EnsureSupportedDockerVersion()
+ flag.Parse()
+ os.Exit(m.Run())
+}
+
// httpRequestSucceeds sends a request to a given url and checks that the status is OK.
func httpRequestSucceeds(client http.Client, server string, port int) error {
url := fmt.Sprintf("http://%s:%d", server, port)
@@ -426,10 +433,10 @@ func TestTmpMount(t *testing.T) {
// Test that it is allowed to mount a file on top of /dev files, e.g.
// /dev/random.
func TestMountOverDev(t *testing.T) {
- if usingVFS2, err := dockerutil.UsingVFS2(); !usingVFS2 {
- t.Skip("VFS1 doesn't allow /dev/random to be mounted.")
- } else if err != nil {
+ if vfs2, err := dockerutil.UsingVFS2(); err != nil {
t.Fatalf("Failed to read config for runtime %s: %v", dockerutil.Runtime(), err)
+ } else if !vfs2 {
+ t.Skip("VFS1 doesn't allow /dev/random to be mounted.")
}
random, err := ioutil.TempFile(testutil.TmpDir(), "random")
@@ -574,11 +581,12 @@ func runIntegrationTest(t *testing.T, capAdd []string, args ...string) {
d := dockerutil.MakeContainer(ctx, t)
defer d.CleanUp(ctx)
- if got, err := d.Run(ctx, dockerutil.RunOpts{
+ opts := dockerutil.RunOpts{
Image: "basic/integrationtest",
WorkDir: "/root",
CapAdd: capAdd,
- }, args...); err != nil {
+ }
+ if got, err := d.Run(ctx, opts, args...); err != nil {
t.Fatalf("docker run failed: %v", err)
} else if got != "" {
t.Errorf("test failed:\n%s", got)
@@ -609,8 +617,174 @@ func TestBindOverlay(t *testing.T) {
}
}
-func TestMain(m *testing.M) {
- dockerutil.EnsureSupportedDockerVersion()
- flag.Parse()
- os.Exit(m.Run())
+func TestStdios(t *testing.T) {
+ if vfs2, err := dockerutil.UsingVFS2(); err != nil {
+ t.Fatalf("Failed to read config for runtime %s: %v", dockerutil.Runtime(), err)
+ } else if !vfs2 {
+ t.Skip("VFS1 doesn't adjust stdios user")
+ }
+
+ ctx := context.Background()
+ d := dockerutil.MakeContainer(ctx, t)
+ defer d.CleanUp(ctx)
+
+ testStdios(t, func(user string, args ...string) (string, error) {
+ defer d.CleanUp(ctx)
+ opts := dockerutil.RunOpts{
+ Image: "basic/alpine",
+ User: user,
+ }
+ return d.Run(ctx, opts, args...)
+ })
+}
+
+func TestStdiosExec(t *testing.T) {
+ if vfs2, err := dockerutil.UsingVFS2(); err != nil {
+ t.Fatalf("Failed to read config for runtime %s: %v", dockerutil.Runtime(), err)
+ } else if !vfs2 {
+ t.Skip("VFS1 doesn't adjust stdios user")
+ }
+
+ ctx := context.Background()
+ d := dockerutil.MakeContainer(ctx, t)
+ defer d.CleanUp(ctx)
+
+ runOpts := dockerutil.RunOpts{Image: "basic/alpine"}
+ if err := d.Spawn(ctx, runOpts, "sleep", "100"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+
+ testStdios(t, func(user string, args ...string) (string, error) {
+ opts := dockerutil.ExecOpts{User: user}
+ return d.Exec(ctx, opts, args...)
+ })
+}
+
+func testStdios(t *testing.T, run func(string, ...string) (string, error)) {
+ const cmd = "stat -L /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 | grep 'Uid:'"
+ got, err := run("123", "/bin/sh", "-c", cmd)
+ if err != nil {
+ t.Fatalf("docker exec failed: %v", err)
+ }
+ if len(got) == 0 {
+ t.Errorf("Unexpected empty output from %q", cmd)
+ }
+ re := regexp.MustCompile(`Uid: \(\s*(\w+)\/.*\)`)
+ for _, line := range strings.SplitN(got, "\n", 3) {
+ t.Logf("stat -L: %s", line)
+ matches := re.FindSubmatch([]byte(line))
+ if len(matches) != 2 {
+ t.Fatalf("wrong output format: %q: matches: %v", line, matches)
+ }
+ if want, got := "123", string(matches[1]); want != got {
+ t.Errorf("wrong user, want: %q, got: %q", want, got)
+ }
+ }
+
+ // Check that stdout and stderr can be open and written to. This checks
+ // that ownership and permissions are correct inside gVisor.
+ got, err = run("456", "/bin/sh", "-c", "echo foobar | tee /proc/self/fd/1 > /proc/self/fd/2")
+ if err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+ t.Logf("echo foobar: %q", got)
+ // Check it repeats twice, once for stdout and once for stderr.
+ if want := "foobar\nfoobar\n"; want != got {
+ t.Errorf("Wrong echo output, want: %q, got: %q", want, got)
+ }
+
+ // Check that timestamps can be changed. Setting timestamps require an extra
+ // write check _after_ the file was opened, and may fail if the underlying
+ // host file is not setup correctly.
+ if _, err := run("789", "touch", "/proc/self/fd/0", "/proc/self/fd/1", "/proc/self/fd/2"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+}
+
+func TestStdiosChown(t *testing.T) {
+ if vfs2, err := dockerutil.UsingVFS2(); err != nil {
+ t.Fatalf("Failed to read config for runtime %s: %v", dockerutil.Runtime(), err)
+ } else if !vfs2 {
+ t.Skip("VFS1 doesn't adjust stdios user")
+ }
+
+ ctx := context.Background()
+ d := dockerutil.MakeContainer(ctx, t)
+ defer d.CleanUp(ctx)
+
+ opts := dockerutil.RunOpts{Image: "basic/alpine"}
+ if _, err := d.Run(ctx, opts, "chown", "123", "/proc/self/fd/0", "/proc/self/fd/1", "/proc/self/fd/2"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+}
+
+func TestUnmount(t *testing.T) {
+ ctx := context.Background()
+ d := dockerutil.MakeContainer(ctx, t)
+ defer d.CleanUp(ctx)
+
+ dir, err := ioutil.TempDir(testutil.TmpDir(), "sub-mount")
+ if err != nil {
+ t.Fatalf("TempDir(): %v", err)
+ }
+ opts := dockerutil.RunOpts{
+ Image: "basic/alpine",
+ Privileged: true, // Required for umount
+ Mounts: []mount.Mount{
+ {
+ Type: mount.TypeBind,
+ Source: dir,
+ Target: "/foo",
+ },
+ },
+ }
+ if _, err := d.Run(ctx, opts, "umount", "/foo"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+}
+
+func TestDeleteInterface(t *testing.T) {
+ if testutil.IsRunningWithHostNet() {
+ t.Skip("not able to remove interfaces on hostnet")
+ }
+
+ ctx := context.Background()
+ d := dockerutil.MakeContainer(ctx, t)
+ defer d.CleanUp(ctx)
+
+ opts := dockerutil.RunOpts{
+ Image: "basic/alpine",
+ CapAdd: []string{"NET_ADMIN"},
+ }
+ if err := d.Spawn(ctx, opts, "sleep", "1000"); err != nil {
+ t.Fatalf("docker run failed: %v", err)
+ }
+
+ // We should be able to remove eth0.
+ output, err := d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", "ip link del dev eth0")
+ if err != nil {
+ t.Fatalf("failed to remove eth0: %s, output: %s", err, output)
+ }
+ // Verify that eth0 is no longer there.
+ output, err = d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", "ip link show")
+ if err != nil {
+ t.Fatalf("docker exec ip link show failed: %s, output: %s", err, output)
+ }
+ if strings.Contains(output, "eth0") {
+ t.Fatalf("failed to remove eth0")
+ }
+
+ // Loopback device can't be removed.
+ output, err = d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", "ip link del dev lo")
+ if err == nil {
+ t.Fatalf("should not remove the loopback device: %v", output)
+ }
+ // Verify that lo is still there.
+ output, err = d.Exec(ctx, dockerutil.ExecOpts{}, "/bin/sh", "-c", "ip link show")
+ if err != nil {
+ t.Fatalf("docker exec ip link show failed: %s, output: %s", err, output)
+ }
+ if !strings.Contains(output, "lo") {
+ t.Fatalf("loopback interface is removed")
+ }
}
diff --git a/test/fsstress/BUILD b/test/fsstress/BUILD
index e74e7fff2..402445a7e 100644
--- a/test/fsstress/BUILD
+++ b/test/fsstress/BUILD
@@ -14,7 +14,11 @@ go_test(
"manual",
"local",
],
- deps = ["//pkg/test/dockerutil"],
+ deps = [
+ "//pkg/test/dockerutil",
+ "//pkg/test/testutil",
+ "@com_github_docker_docker//api/types/mount:go_default_library",
+ ],
)
go_library(
diff --git a/test/fsstress/fsstress_test.go b/test/fsstress/fsstress_test.go
index d53c8f90d..d9513c42c 100644
--- a/test/fsstress/fsstress_test.go
+++ b/test/fsstress/fsstress_test.go
@@ -18,6 +18,8 @@ package fsstress
import (
"context"
"flag"
+ "fmt"
+ "io/ioutil"
"math/rand"
"os"
"strconv"
@@ -25,7 +27,9 @@ import (
"testing"
"time"
+ "github.com/docker/docker/api/types/mount"
"gvisor.dev/gvisor/pkg/test/dockerutil"
+ "gvisor.dev/gvisor/pkg/test/testutil"
)
func init() {
@@ -42,6 +46,7 @@ type config struct {
operations string
processes string
target string
+ mounts []mount.Mount
}
func fsstress(t *testing.T, conf config) {
@@ -52,8 +57,19 @@ func fsstress(t *testing.T, conf config) {
const image = "basic/fsstress"
seed := strconv.FormatUint(uint64(rand.Uint32()), 10)
args := []string{"-d", conf.target, "-n", conf.operations, "-p", conf.processes, "-s", seed, "-X"}
- t.Logf("Repro: docker run --rm --runtime=%s gvisor.dev/images/%s %s", dockerutil.Runtime(), image, strings.Join(args, " "))
- out, err := d.Run(ctx, dockerutil.RunOpts{Image: image}, args...)
+ opts := dockerutil.RunOpts{
+ Image: image,
+ Mounts: conf.mounts,
+ }
+ var mounts string
+ if len(conf.mounts) > 0 {
+ mounts = " -v "
+ for _, m := range conf.mounts {
+ mounts += fmt.Sprintf("-v <any_dir>:%s", m.Target)
+ }
+ }
+ t.Logf("Repro: docker run --rm --runtime=%s%s gvisor.dev/images/%s %s", dockerutil.Runtime(), mounts, image, strings.Join(args, " "))
+ out, err := d.Run(ctx, opts, args...)
if err != nil {
t.Fatalf("docker run failed: %v\noutput: %s", err, out)
}
@@ -64,6 +80,39 @@ func fsstress(t *testing.T, conf config) {
}
}
+func TestFsstressGofer(t *testing.T) {
+ // This takes between 30-60s to run on my machine. Adjust as needed.
+ cfg := config{
+ operations: "500",
+ processes: "20",
+ target: "/test",
+ }
+ fsstress(t, cfg)
+}
+
+func TestFsstressGoferShared(t *testing.T) {
+ dir, err := ioutil.TempDir(testutil.TmpDir(), "fsstress")
+ if err != nil {
+ t.Fatalf("ioutil.TempDir() failed: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ // This takes between 30-60s to run on my machine. Adjust as needed.
+ cfg := config{
+ operations: "500",
+ processes: "20",
+ target: "/test",
+ mounts: []mount.Mount{
+ {
+ Source: dir,
+ Target: "/test",
+ Type: "bind",
+ },
+ },
+ }
+ fsstress(t, cfg)
+}
+
func TestFsstressTmpfs(t *testing.T) {
// This takes between 10s to run on my machine. Adjust as needed.
cfg := config{
diff --git a/test/packetimpact/README.md b/test/packetimpact/README.md
index fe0976ba5..3f0f9d155 100644
--- a/test/packetimpact/README.md
+++ b/test/packetimpact/README.md
@@ -16,7 +16,7 @@ Packetimpact aims to provide:
* A **multi-platform** solution that can test both Linux and gVisor.
* **Conciseness** on par with packetdrill scripts.
* **Control-flow** like for loops, conditionals, and variables.
-* **Flexibilty** to specify every byte in a packet or use multiple sockets.
+* **Flexibility** to specify every byte in a packet or use multiple sockets.
## How to run packetimpact tests?
@@ -177,7 +177,7 @@ message SocketResponse {
The test bench does most of the work in a test. It is a Go program that compiles
on the host and is copied by the script into test bench's container. It is a
regular [go unit test](https://golang.org/pkg/testing/) that imports the test
-bench framework. The test bench framwork is based on three basic utilities:
+bench framework. The test bench framework is based on three basic utilities:
* Commanding the DUT to run POSIX commands and return responses.
* Sending raw packets to the DUT on the test network.
@@ -475,9 +475,9 @@ type layerState interface {
// as it was sent is available.
sent(sent Layer) error
- // received updates the layerState based on a Layer that is receieved. The
+ // received updates the layerState based on a Layer that is received. The
// input is a Layer with all prev and next pointers populated so that the
- // entire frame as it was receieved is available.
+ // entire frame as it was received is available.
received(received Layer) error
// close frees associated resources held by the LayerState.
diff --git a/test/packetimpact/dut/posix_server.cc b/test/packetimpact/dut/posix_server.cc
index ea83bbe72..49f41c887 100644
--- a/test/packetimpact/dut/posix_server.cc
+++ b/test/packetimpact/dut/posix_server.cc
@@ -28,10 +28,10 @@
#include <iostream>
#include <unordered_map>
+#include "absl/strings/str_format.h"
#include "include/grpcpp/security/server_credentials.h"
#include "include/grpcpp/server_builder.h"
#include "include/grpcpp/server_context.h"
-#include "absl/strings/str_format.h"
#include "test/packetimpact/proto/posix_server.grpc.pb.h"
#include "test/packetimpact/proto/posix_server.pb.h"
diff --git a/test/packetimpact/testbench/dut.go b/test/packetimpact/testbench/dut.go
index 0cac0bf1b..7e89ba2b3 100644
--- a/test/packetimpact/testbench/dut.go
+++ b/test/packetimpact/testbench/dut.go
@@ -180,9 +180,7 @@ func (dut *DUT) CreateListener(t *testing.T, typ, proto, backlog int32) (int32,
func (dut *DUT) Accept(t *testing.T, sockfd int32) (int32, unix.Sockaddr) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- fd, sa, err := dut.AcceptWithErrno(ctx, t, sockfd)
+ fd, sa, err := dut.AcceptWithErrno(context.Background(), t, sockfd)
if fd < 0 {
t.Fatalf("failed to accept: %s", err)
}
@@ -209,9 +207,7 @@ func (dut *DUT) AcceptWithErrno(ctx context.Context, t *testing.T, sockfd int32)
func (dut *DUT) Bind(t *testing.T, fd int32, sa unix.Sockaddr) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.BindWithErrno(ctx, t, fd, sa)
+ ret, err := dut.BindWithErrno(context.Background(), t, fd, sa)
if ret != 0 {
t.Fatalf("failed to bind socket: %s", err)
}
@@ -238,9 +234,7 @@ func (dut *DUT) BindWithErrno(ctx context.Context, t *testing.T, fd int32, sa un
func (dut *DUT) Close(t *testing.T, fd int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.CloseWithErrno(ctx, t, fd)
+ ret, err := dut.CloseWithErrno(context.Background(), t, fd)
if ret != 0 {
t.Fatalf("failed to close: %s", err)
}
@@ -266,9 +260,7 @@ func (dut *DUT) CloseWithErrno(ctx context.Context, t *testing.T, fd int32) (int
func (dut *DUT) Connect(t *testing.T, fd int32, sa unix.Sockaddr) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ConnectWithErrno(ctx, t, fd, sa)
+ ret, err := dut.ConnectWithErrno(context.Background(), t, fd, sa)
// Ignore 'operation in progress' error that can be returned when the socket
// is non-blocking.
if err != unix.EINPROGRESS && ret != 0 {
@@ -297,9 +289,7 @@ func (dut *DUT) ConnectWithErrno(ctx context.Context, t *testing.T, fd int32, sa
func (dut *DUT) GetSockName(t *testing.T, sockfd int32) unix.Sockaddr {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, sa, err := dut.GetSockNameWithErrno(ctx, t, sockfd)
+ ret, sa, err := dut.GetSockNameWithErrno(context.Background(), t, sockfd)
if ret != 0 {
t.Fatalf("failed to getsockname: %s", err)
}
@@ -349,9 +339,7 @@ func (dut *DUT) getSockOpt(ctx context.Context, t *testing.T, sockfd, level, opt
func (dut *DUT) GetSockOpt(t *testing.T, sockfd, level, optname, optlen int32) []byte {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, optval, err := dut.GetSockOptWithErrno(ctx, t, sockfd, level, optname, optlen)
+ ret, optval, err := dut.GetSockOptWithErrno(context.Background(), t, sockfd, level, optname, optlen)
if ret != 0 {
t.Fatalf("failed to GetSockOpt: %s", err)
}
@@ -378,9 +366,7 @@ func (dut *DUT) GetSockOptWithErrno(ctx context.Context, t *testing.T, sockfd, l
func (dut *DUT) GetSockOptInt(t *testing.T, sockfd, level, optname int32) int32 {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, intval, err := dut.GetSockOptIntWithErrno(ctx, t, sockfd, level, optname)
+ ret, intval, err := dut.GetSockOptIntWithErrno(context.Background(), t, sockfd, level, optname)
if ret != 0 {
t.Fatalf("failed to GetSockOptInt: %s", err)
}
@@ -405,9 +391,7 @@ func (dut *DUT) GetSockOptIntWithErrno(ctx context.Context, t *testing.T, sockfd
func (dut *DUT) GetSockOptTimeval(t *testing.T, sockfd, level, optname int32) unix.Timeval {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, timeval, err := dut.GetSockOptTimevalWithErrno(ctx, t, sockfd, level, optname)
+ ret, timeval, err := dut.GetSockOptTimevalWithErrno(context.Background(), t, sockfd, level, optname)
if ret != 0 {
t.Fatalf("failed to GetSockOptTimeval: %s", err)
}
@@ -434,9 +418,7 @@ func (dut *DUT) GetSockOptTimevalWithErrno(ctx context.Context, t *testing.T, so
func (dut *DUT) GetSockOptTCPInfo(t *testing.T, sockfd int32) linux.TCPInfo {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, info, err := dut.GetSockOptTCPInfoWithErrno(ctx, t, sockfd)
+ ret, info, err := dut.GetSockOptTCPInfoWithErrno(context.Background(), t, sockfd)
if ret != 0 || err != unix.Errno(0) {
t.Fatalf("failed to GetSockOptTCPInfo: %s", err)
}
@@ -463,9 +445,7 @@ func (dut *DUT) GetSockOptTCPInfoWithErrno(ctx context.Context, t *testing.T, so
func (dut *DUT) Listen(t *testing.T, sockfd, backlog int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ListenWithErrno(ctx, t, sockfd, backlog)
+ ret, err := dut.ListenWithErrno(context.Background(), t, sockfd, backlog)
if ret != 0 {
t.Fatalf("failed to listen: %s", err)
}
@@ -510,13 +490,7 @@ func (dut *DUT) PollOne(t *testing.T, fd int32, events int16, timeout time.Durat
func (dut *DUT) Poll(t *testing.T, pfds []unix.PollFd, timeout time.Duration) []unix.PollFd {
t.Helper()
- ctx := context.Background()
- var cancel context.CancelFunc
- if timeout >= 0 {
- ctx, cancel = context.WithTimeout(ctx, timeout+RPCTimeout)
- defer cancel()
- }
- ret, result, err := dut.PollWithErrno(ctx, t, pfds, timeout)
+ ret, result, err := dut.PollWithErrno(context.Background(), t, pfds, timeout)
if ret < 0 {
t.Fatalf("failed to poll: %s", err)
}
@@ -559,9 +533,7 @@ func (dut *DUT) PollWithErrno(ctx context.Context, t *testing.T, pfds []unix.Pol
func (dut *DUT) Send(t *testing.T, sockfd int32, buf []byte, flags int32) int32 {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SendWithErrno(ctx, t, sockfd, buf, flags)
+ ret, err := dut.SendWithErrno(context.Background(), t, sockfd, buf, flags)
if ret == -1 {
t.Fatalf("failed to send: %s", err)
}
@@ -590,9 +562,7 @@ func (dut *DUT) SendWithErrno(ctx context.Context, t *testing.T, sockfd int32, b
func (dut *DUT) SendTo(t *testing.T, sockfd int32, buf []byte, flags int32, destAddr unix.Sockaddr) int32 {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SendToWithErrno(ctx, t, sockfd, buf, flags, destAddr)
+ ret, err := dut.SendToWithErrno(context.Background(), t, sockfd, buf, flags, destAddr)
if ret == -1 {
t.Fatalf("failed to sendto: %s", err)
}
@@ -625,10 +595,8 @@ func (dut *DUT) SetNonBlocking(t *testing.T, fd int32, nonblocking bool) {
Fd: fd,
Nonblocking: nonblocking,
}
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- resp, err := dut.posixServer.SetNonblocking(ctx, req)
+ resp, err := dut.posixServer.SetNonblocking(context.Background(), req)
if err != nil {
t.Fatalf("failed to call SetNonblocking: %s", err)
}
@@ -661,9 +629,7 @@ func (dut *DUT) setSockOpt(ctx context.Context, t *testing.T, sockfd, level, opt
func (dut *DUT) SetSockOpt(t *testing.T, sockfd, level, optname int32, optval []byte) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptWithErrno(ctx, t, sockfd, level, optname, optval)
+ ret, err := dut.SetSockOptWithErrno(context.Background(), t, sockfd, level, optname, optval)
if ret != 0 {
t.Fatalf("failed to SetSockOpt: %s", err)
}
@@ -684,9 +650,7 @@ func (dut *DUT) SetSockOptWithErrno(ctx context.Context, t *testing.T, sockfd, l
func (dut *DUT) SetSockOptInt(t *testing.T, sockfd, level, optname, optval int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptIntWithErrno(ctx, t, sockfd, level, optname, optval)
+ ret, err := dut.SetSockOptIntWithErrno(context.Background(), t, sockfd, level, optname, optval)
if ret != 0 {
t.Fatalf("failed to SetSockOptInt: %s", err)
}
@@ -705,9 +669,7 @@ func (dut *DUT) SetSockOptIntWithErrno(ctx context.Context, t *testing.T, sockfd
func (dut *DUT) SetSockOptTimeval(t *testing.T, sockfd, level, optname int32, tv *unix.Timeval) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.SetSockOptTimevalWithErrno(ctx, t, sockfd, level, optname, tv)
+ ret, err := dut.SetSockOptTimevalWithErrno(context.Background(), t, sockfd, level, optname, tv)
if ret != 0 {
t.Fatalf("failed to SetSockOptTimeval: %s", err)
}
@@ -746,8 +708,7 @@ func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32,
Type: typ,
Protocol: proto,
}
- ctx := context.Background()
- resp, err := dut.posixServer.Socket(ctx, req)
+ resp, err := dut.posixServer.Socket(context.Background(), req)
if err != nil {
t.Fatalf("failed to call Socket: %s", err)
}
@@ -760,9 +721,7 @@ func (dut *DUT) SocketWithErrno(t *testing.T, domain, typ, proto int32) (int32,
func (dut *DUT) Recv(t *testing.T, sockfd, len, flags int32) []byte {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, buf, err := dut.RecvWithErrno(ctx, t, sockfd, len, flags)
+ ret, buf, err := dut.RecvWithErrno(context.Background(), t, sockfd, len, flags)
if ret == -1 {
t.Fatalf("failed to recv: %s", err)
}
@@ -805,9 +764,7 @@ func (dut *DUT) SetSockLingerOption(t *testing.T, sockfd int32, timeout time.Dur
func (dut *DUT) Shutdown(t *testing.T, fd, how int32) {
t.Helper()
- ctx, cancel := context.WithTimeout(context.Background(), RPCTimeout)
- defer cancel()
- ret, err := dut.ShutdownWithErrno(ctx, t, fd, how)
+ ret, err := dut.ShutdownWithErrno(context.Background(), t, fd, how)
if ret != 0 {
t.Fatalf("failed to shutdown(%d, %d): %s", fd, how, err)
}
diff --git a/test/packetimpact/testbench/dut_client.go b/test/packetimpact/testbench/dut_client.go
index 0fc3d97b4..3b69b28aa 100644
--- a/test/packetimpact/testbench/dut_client.go
+++ b/test/packetimpact/testbench/dut_client.go
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+//go:build go1.1
+// +build go1.1
+
package testbench
import (
diff --git a/test/packetimpact/testbench/testbench.go b/test/packetimpact/testbench/testbench.go
index caa389780..38ae9c1d7 100644
--- a/test/packetimpact/testbench/testbench.go
+++ b/test/packetimpact/testbench/testbench.go
@@ -31,8 +31,6 @@ var (
Native = false
// RPCKeepalive is the gRPC keepalive.
RPCKeepalive = 10 * time.Second
- // RPCTimeout is the gRPC timeout.
- RPCTimeout = 100 * time.Millisecond
// dutInfosJSON is the json string that describes information about all the
// duts available to use.
@@ -124,7 +122,6 @@ func (n *DUTTestNet) SubnetBroadcast() net.IP {
// functions.
func registerFlags(fs *flag.FlagSet) {
fs.BoolVar(&Native, "native", Native, "whether the test is running natively")
- fs.DurationVar(&RPCTimeout, "rpc_timeout", RPCTimeout, "gRPC timeout")
fs.DurationVar(&RPCKeepalive, "rpc_keepalive", RPCKeepalive, "gRPC keepalive")
fs.StringVar(&dutInfosJSON, "dut_infos_json", dutInfosJSON, "json that describes the DUTs")
}
diff --git a/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go b/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
index 00e0f7690..a9ffafc74 100644
--- a/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
+++ b/test/packetimpact/tests/generic_dgram_socket_send_recv_test.go
@@ -48,7 +48,6 @@ func maxUDPPayloadSize(addr net.IP) int {
func init() {
testbench.Initialize(flag.CommandLine)
- testbench.RPCTimeout = 500 * time.Millisecond
}
func expectedEthLayer(t *testing.T, dut testbench.DUT, socketFD int32, sendTo net.IP) testbench.Layer {
@@ -437,9 +436,7 @@ func (test *icmpV6Test) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net
copy(destSockaddr.Addr[:], sendTo.To16())
// Tell the DUT to send a packet out the ICMPv6 socket.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
- gotRet, gotErrno := dut.SendToWithErrno(ctx, t, env.socketFD, bytes, 0, &destSockaddr)
+ gotRet, gotErrno := dut.SendToWithErrno(context.Background(), t, env.socketFD, bytes, 0, &destSockaddr)
if gotErrno != wantErrno {
t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (_, %s), want = (_, %s)", env.socketFD, sendTo, gotErrno, wantErrno)
@@ -677,9 +674,7 @@ func (test *udpTest) Send(t *testing.T, dut testbench.DUT, bindTo, sendTo net.IP
}
// Tell the DUT to send a packet out the UDP socket.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
- gotRet, gotErrno := dut.SendToWithErrno(ctx, t, env.socketFD, payload, 0, destSockaddr)
+ gotRet, gotErrno := dut.SendToWithErrno(context.Background(), t, env.socketFD, payload, 0, destSockaddr)
if gotErrno != wantErrno {
t.Fatalf("got dut.SendToWithErrno(_, _, %d, _, _, %s) = (_, %s), want = (_, %s)", env.socketFD, sendTo, gotErrno, wantErrno)
diff --git a/test/packetimpact/tests/tcp_connect_icmp_error_test.go b/test/packetimpact/tests/tcp_connect_icmp_error_test.go
index 3b4c4cd63..15d603328 100644
--- a/test/packetimpact/tests/tcp_connect_icmp_error_test.go
+++ b/test/packetimpact/tests/tcp_connect_icmp_error_test.go
@@ -15,9 +15,7 @@
package tcp_connect_icmp_error_test
import (
- "context"
"flag"
- "sync"
"testing"
"time"
@@ -66,35 +64,38 @@ func TestTCPConnectICMPError(t *testing.T) {
t.Fatalf("expected SYN, %s", err)
}
- done := make(chan bool)
- defer close(done)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ // Continuously try to read the ICMP error in an attempt to trigger a race
+ // condition.
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- _, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
for {
select {
case <-done:
return
default:
- if errno := dut.GetSockOptInt(t, clientFD, unix.SOL_SOCKET, unix.SO_ERROR); errno != 0 {
- return
- }
}
+ const want = unix.EHOSTUNREACH
+ switch got := unix.Errno(dut.GetSockOptInt(t, clientFD, unix.SOL_SOCKET, unix.SO_ERROR)); got {
+ case unix.Errno(0):
+ continue
+ case want:
+ return
+ default:
+ t.Fatalf("got SO_ERROR = %s, want %s", got, want)
+ }
+
}
}()
- block.Wait()
+ <-start
sendICMPError(t, &conn, tcp)
dut.PollOne(t, clientFD, unix.POLLHUP, time.Second)
+ <-done
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
// The DUT should reply with RST to our ACK as the state should have
diff --git a/test/packetimpact/tests/tcp_info_test.go b/test/packetimpact/tests/tcp_info_test.go
index b7514e846..5410cc368 100644
--- a/test/packetimpact/tests/tcp_info_test.go
+++ b/test/packetimpact/tests/tcp_info_test.go
@@ -47,7 +47,7 @@ func TestTCPInfo(t *testing.T) {
samplePayload := &testbench.Payload{Bytes: sampleData}
dut.Send(t, acceptFD, sampleData, 0)
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
}
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)})
@@ -55,20 +55,23 @@ func TestTCPInfo(t *testing.T) {
if got, want := uint32(info.State), linux.TCP_ESTABLISHED; got != want {
t.Fatalf("got %d want %d", got, want)
}
- rtt := time.Duration(info.RTT) * time.Microsecond
- rttvar := time.Duration(info.RTTVar) * time.Microsecond
- rto := time.Duration(info.RTO) * time.Microsecond
- if rtt == 0 || rttvar == 0 || rto == 0 {
- t.Errorf("expected rtt(%v), rttvar(%v) and rto(%v) to be greater than zero", rtt, rttvar, rto)
+ if info.RTT == 0 {
+ t.Errorf("got RTT=0, want nonzero")
+ }
+ if info.RTTVar == 0 {
+ t.Errorf("got RTTVar=0, want nonzero")
+ }
+ if info.RTO == 0 {
+ t.Errorf("got RTO=0, want nonzero")
}
if info.ReordSeen != 0 {
- t.Errorf("expected the connection to not have any reordering, got: %v want: 0", info.ReordSeen)
+ t.Errorf("expected the connection to not have any reordering, got: %d want: 0", info.ReordSeen)
}
if info.SndCwnd == 0 {
t.Errorf("expected send congestion window to be greater than zero")
}
if info.CaState != linux.TCP_CA_Open {
- t.Errorf("expected the connection to be in open state, got: %v want: %v", info.CaState, linux.TCP_CA_Open)
+ t.Errorf("expected the connection to be in open state, got: %d want: %d", info.CaState, linux.TCP_CA_Open)
}
if t.Failed() {
@@ -80,20 +83,20 @@ func TestTCPInfo(t *testing.T) {
seq := testbench.Uint32(uint32(*conn.RemoteSeqNum(t)))
dut.Send(t, acceptFD, sampleData, 0)
if _, err := conn.ExpectData(t, &testbench.TCP{}, samplePayload, time.Second); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
}
- // Expect retransmission of the packet within 1.5*RTO.
- timeout := time.Duration(float64(info.RTO)*1.5) * time.Microsecond
+ // Given a generous retransmission timeout.
+ timeout := time.Duration(info.RTO) * 2 * time.Microsecond
if _, err := conn.ExpectData(t, &testbench.TCP{SeqNum: seq}, samplePayload, timeout); err != nil {
- t.Fatalf("expected a packet with payload %v: %s", samplePayload, err)
+ t.Fatalf("expected a packet with payload %s: %s", samplePayload, err)
}
info = dut.GetSockOptTCPInfo(t, acceptFD)
if info.CaState != linux.TCP_CA_Loss {
- t.Errorf("expected the connection to be in loss recovery, got: %v want: %v", info.CaState, linux.TCP_CA_Loss)
+ t.Errorf("expected the connection to be in loss recovery, got: %d want: %d", info.CaState, linux.TCP_CA_Loss)
}
if info.SndCwnd != 1 {
- t.Errorf("expected send congestion window to be 1, got: %v %v", info.SndCwnd)
+ t.Errorf("expected send congestion window to be 1, got: %d", info.SndCwnd)
}
}
diff --git a/test/packetimpact/tests/tcp_linger_test.go b/test/packetimpact/tests/tcp_linger_test.go
index 88942904d..46b5ca5d8 100644
--- a/test/packetimpact/tests/tcp_linger_test.go
+++ b/test/packetimpact/tests/tcp_linger_test.go
@@ -98,20 +98,19 @@ func TestTCPLingerNonZeroTimeout(t *testing.T) {
dut.SetSockLingerOption(t, acceptFD, lingerDuration, tt.lingerOn)
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
+ dut.CloseWithErrno(context.Background(), t, acceptFD)
+ elapsed := time.Since(start)
+
+ expectedMaximum := time.Second
+ if tt.lingerOn {
+ expectedMaximum += lingerDuration
+ if elapsed < lingerDuration {
+ t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
+ }
+ }
+ if elapsed >= expectedMaximum {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagFin | header.TCPFlagAck)}, time.Second); err != nil {
@@ -144,20 +143,19 @@ func TestTCPLingerSendNonZeroTimeout(t *testing.T) {
sampleData := []byte("Sample Data")
dut.Send(t, acceptFD, sampleData, 0)
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
+ dut.CloseWithErrno(context.Background(), t, acceptFD)
+ elapsed := time.Since(start)
+
+ expectedMaximum := time.Second
+ if tt.lingerOn {
+ expectedMaximum += lingerDuration
+ if elapsed < lingerDuration {
+ t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
+ }
+ }
+ if elapsed >= expectedMaximum {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
samplePayload := &testbench.Payload{Bytes: sampleData}
@@ -221,20 +219,19 @@ func TestTCPLingerShutdownSendNonZeroTimeout(t *testing.T) {
dut.Shutdown(t, acceptFD, unix.SHUT_RDWR)
- // Increase timeout as Close will take longer time to
- // return when SO_LINGER is set with non-zero timeout.
- timeout := lingerDuration + 1*time.Second
- ctx, cancel := context.WithTimeout(context.Background(), timeout)
- defer cancel()
start := time.Now()
- dut.CloseWithErrno(ctx, t, acceptFD)
- end := time.Now()
- diff := end.Sub(start)
-
- if tt.lingerOn && diff < lingerDuration {
- t.Errorf("expected close to return after %v seconds, but returned sooner", lingerDuration)
- } else if !tt.lingerOn && diff > 1*time.Second {
- t.Errorf("expected close to return within a second, but returned later")
+ dut.CloseWithErrno(context.Background(), t, acceptFD)
+ elapsed := time.Since(start)
+
+ expectedMaximum := time.Second
+ if tt.lingerOn {
+ expectedMaximum += lingerDuration
+ if elapsed < lingerDuration {
+ t.Errorf("expected close to take at least %s, but took %s", lingerDuration, elapsed)
+ }
+ }
+ if elapsed >= expectedMaximum {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
samplePayload := &testbench.Payload{Bytes: sampleData}
@@ -259,9 +256,10 @@ func TestTCPLingerNonEstablished(t *testing.T) {
// and return immediately.
start := time.Now()
dut.CloseWithErrno(context.Background(), t, newFD)
- diff := time.Since(start)
+ elapsed := time.Since(start)
- if diff > lingerDuration {
- t.Errorf("expected close to return within %s, but returned after %s", lingerDuration, diff)
+ expectedMaximum := time.Second
+ if elapsed >= time.Second {
+ t.Errorf("expected close to take at most %s, but took %s", expectedMaximum, elapsed)
}
}
diff --git a/test/packetimpact/tests/tcp_network_unreachable_test.go b/test/packetimpact/tests/tcp_network_unreachable_test.go
index 60a2dbf3d..e92e6aa9b 100644
--- a/test/packetimpact/tests/tcp_network_unreachable_test.go
+++ b/test/packetimpact/tests/tcp_network_unreachable_test.go
@@ -41,11 +41,9 @@ func TestTCPSynSentUnreachable(t *testing.T) {
defer conn.Close(t)
// Bring the DUT to SYN-SENT state with a non-blocking connect.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
sa := unix.SockaddrInet4{Port: int(port)}
copy(sa.Addr[:], dut.Net.LocalIPv4)
- if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != unix.EINPROGRESS {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
t.Errorf("got connect() = %v, want EINPROGRESS", err)
}
@@ -86,14 +84,12 @@ func TestTCPSynSentUnreachable6(t *testing.T) {
defer conn.Close(t)
// Bring the DUT to SYN-SENT state with a non-blocking connect.
- ctx, cancel := context.WithTimeout(context.Background(), testbench.RPCTimeout)
- defer cancel()
sa := unix.SockaddrInet6{
Port: int(conn.SrcPort()),
ZoneId: dut.Net.RemoteDevID,
}
copy(sa.Addr[:], dut.Net.LocalIPv6)
- if _, err := dut.ConnectWithErrno(ctx, t, clientFD, &sa); err != unix.EINPROGRESS {
+ if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
t.Errorf("got connect() = %v, want EINPROGRESS", err)
}
diff --git a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
index 1c8b72ebe..974c15384 100644
--- a/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
+++ b/test/packetimpact/tests/tcp_queue_send_recv_in_syn_sent_test.go
@@ -20,7 +20,6 @@ import (
"encoding/hex"
"errors"
"flag"
- "sync"
"testing"
"time"
@@ -54,37 +53,39 @@ func TestQueueSendInSynSentHandshake(t *testing.T) {
// Test blocking send.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue SEND call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, err := dut.SendWithErrno(ctx, t, socket, sampleData, 0)
- if n == -1 {
+ if _, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0); err != unix.Errno(0) {
t.Errorf("failed to send on DUT: %s", err)
- return
}
}()
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
// request and the system actually being blocked.
time.Sleep(100 * time.Millisecond)
+ select {
+ case <-done:
+ t.Fatal("expected send to be blocked in SYN-SENT")
+ default:
+ }
+
// Bring the connection to Established.
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn | header.TCPFlagAck)})
+
+ <-done
+
// Expect the data from the DUT's enqueued send request.
//
// On Linux, this can be piggybacked with the ACK completing the
@@ -126,21 +127,16 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
// Test blocking read.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue RECEIVE call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, buff, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
- if n == -1 {
+ n, buff, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0)
+ if err != unix.Errno(0) {
t.Errorf("failed to recv on DUT: %s", err)
return
}
@@ -151,7 +147,8 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
+
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
@@ -169,6 +166,8 @@ func TestQueueRecvInSynSentHandshake(t *testing.T) {
if _, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagAck)}, time.Second); err != nil {
t.Fatalf("expected an ACK from DUT, but got none: %s", err)
}
+
+ <-done
}
// TestQueueSendInSynSentRST tests send behavior when the TCP state
@@ -192,20 +191,15 @@ func TestQueueSendInSynSentRST(t *testing.T) {
// Test blocking send.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue SEND call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, err := dut.SendWithErrno(ctx, t, socket, sampleData, 0)
+ n, err := dut.SendWithErrno(context.Background(), t, socket, sampleData, 0)
if err != unix.ECONNREFUSED {
t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
}
@@ -216,14 +210,23 @@ func TestQueueSendInSynSentRST(t *testing.T) {
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
+
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
// request and the system actually being blocked.
time.Sleep(100 * time.Millisecond)
+ select {
+ case <-done:
+ t.Fatal("expected send to be blocked in SYN-SENT")
+ default:
+ }
+
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
+
+ <-done
}
// TestQueueRecvInSynSentRST tests recv behavior when the TCP state
@@ -251,20 +254,15 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
// Test blocking read.
dut.SetNonBlocking(t, socket, false)
- var wg sync.WaitGroup
- defer wg.Wait()
- wg.Add(1)
- var block sync.WaitGroup
- block.Add(1)
+ start := make(chan struct{})
+ done := make(chan struct{})
go func() {
- defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
- defer cancel()
+ defer close(done)
- block.Done()
+ close(start)
// Issue RECEIVE call in SYN-SENT, this should be queued for
// process until the connection is established.
- n, _, err := dut.RecvWithErrno(ctx, t, socket, int32(len(sampleData)), 0)
+ n, _, err := dut.RecvWithErrno(context.Background(), t, socket, int32(len(sampleData)), 0)
if err != unix.ECONNREFUSED {
t.Errorf("expected error %s, got %s", unix.ECONNREFUSED, err)
}
@@ -275,7 +273,8 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
// Wait for the goroutine to be scheduled and before it
// blocks on endpoint send/receive.
- block.Wait()
+ <-start
+
// The following sleep is used to prevent the connection
// from being established before we are blocked: there is
// still a small time window between we sending the RPC
@@ -283,4 +282,5 @@ func TestQueueRecvInSynSentRST(t *testing.T) {
time.Sleep(100 * time.Millisecond)
conn.Send(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagRst | header.TCPFlagAck)})
+ <-done
}
diff --git a/test/packetimpact/tests/udp_icmp_error_propagation_test.go b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
index 087aeb66e..556cee1d9 100644
--- a/test/packetimpact/tests/udp_icmp_error_propagation_test.go
+++ b/test/packetimpact/tests/udp_icmp_error_propagation_test.go
@@ -141,8 +141,6 @@ func testRecv(ctx context.Context, t *testing.T, d testData) {
d.conn.Send(t, testbench.UDP{})
if d.wantErrno != unix.Errno(0) {
- ctx, cancel := context.WithTimeout(ctx, time.Second)
- defer cancel()
ret, _, err := d.dut.RecvWithErrno(ctx, t, d.remoteFD, 100, 0)
if ret != -1 {
t.Fatalf("recv after ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", d.wantErrno)
@@ -157,7 +155,7 @@ func testRecv(ctx context.Context, t *testing.T, d testData) {
// testSendTo tests observing the ICMP error through the send syscall. If
// wantErrno is non-zero, the first send should fail and a subsequent send
-// should suceed; while if wantErrno is zero then the first send should just
+// should succeed; while if wantErrno is zero then the first send should just
// succeed.
func testSendTo(ctx context.Context, t *testing.T, d testData) {
// Check that sending on the clean socket works.
@@ -167,8 +165,6 @@ func testSendTo(ctx context.Context, t *testing.T, d testData) {
}
if d.wantErrno != unix.Errno(0) {
- ctx, cancel := context.WithTimeout(ctx, time.Second)
- defer cancel()
ret, err := d.dut.SendToWithErrno(ctx, t, d.remoteFD, nil, 0, d.conn.LocalAddr(t))
if ret != -1 {
@@ -315,10 +311,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
defer wg.Done()
if wantErrno != unix.Errno(0) {
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- ret, _, err := dut.RecvWithErrno(ctx, t, remoteFD, 100, 0)
+ ret, _, err := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0)
if ret != -1 {
t.Errorf("recv during ICMP error succeeded unexpectedly, expected (%[1]d) %[1]v", wantErrno)
return
@@ -329,10 +322,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
}
}
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- if ret, _, err := dut.RecvWithErrno(ctx, t, remoteFD, 100, 0); ret == -1 {
+ if ret, _, err := dut.RecvWithErrno(context.Background(), t, remoteFD, 100, 0); ret == -1 {
t.Errorf("recv after ICMP error failed with (%[1]d) %[1]", err)
}
}()
@@ -340,10 +330,7 @@ func TestICMPErrorDuringUDPRecv(t *testing.T) {
go func() {
defer wg.Done()
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
-
- if ret, _, err := dut.RecvWithErrno(ctx, t, cleanFD, 100, 0); ret == -1 {
+ if ret, _, err := dut.RecvWithErrno(context.Background(), t, cleanFD, 100, 0); ret == -1 {
t.Errorf("recv on clean socket failed with (%[1]d) %[1]", err)
}
}()
diff --git a/test/perf/BUILD b/test/perf/BUILD
index 75b5003e2..59923da47 100644
--- a/test/perf/BUILD
+++ b/test/perf/BUILD
@@ -70,6 +70,13 @@ syscall_test(
)
syscall_test(
+ size = "large",
+ add_overlay = True,
+ debug = False,
+ test = "//test/perf/linux:dup_benchmark",
+)
+
+syscall_test(
debug = False,
test = "//test/perf/linux:pipe_benchmark",
)
@@ -139,3 +146,38 @@ syscall_test(
debug = False,
test = "//test/perf/linux:write_benchmark",
)
+
+syscall_test(
+ size = "large",
+ debug = False,
+ test = "//test/perf/linux:verity_open_benchmark",
+ vfs1 = False,
+)
+
+syscall_test(
+ size = "large",
+ debug = False,
+ test = "//test/perf/linux:verity_read_benchmark",
+ vfs1 = False,
+)
+
+syscall_test(
+ size = "large",
+ debug = False,
+ test = "//test/perf/linux:verity_randread_benchmark",
+ vfs1 = False,
+)
+
+syscall_test(
+ size = "large",
+ debug = False,
+ test = "//test/perf/linux:verity_open_read_close_benchmark",
+ vfs1 = False,
+)
+
+syscall_test(
+ size = "large",
+ debug = False,
+ test = "//test/perf/linux:verity_stat_benchmark",
+ vfs1 = False,
+)
diff --git a/test/perf/linux/BUILD b/test/perf/linux/BUILD
index dd1d2438c..020a69ab5 100644
--- a/test/perf/linux/BUILD
+++ b/test/perf/linux/BUILD
@@ -27,10 +27,10 @@ cc_binary(
deps = [
gbenchmark,
gtest,
- "//test/syscalls/linux:socket_test_util",
"//test/util:file_descriptor",
"//test/util:logging",
"//test/util:posix_error",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
@@ -109,6 +109,22 @@ cc_binary(
)
cc_binary(
+ name = "dup_benchmark",
+ testonly = 1,
+ srcs = [
+ "dup_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ ],
+)
+
+cc_binary(
name = "read_benchmark",
testonly = 1,
srcs = [
@@ -370,3 +386,99 @@ cc_binary(
"//test/util:test_main",
],
)
+
+cc_binary(
+ name = "verity_open_benchmark",
+ testonly = 1,
+ srcs = [
+ "verity_open_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:capability_util",
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ ],
+)
+
+cc_binary(
+ name = "verity_read_benchmark",
+ testonly = 1,
+ srcs = [
+ "verity_read_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:capability_util",
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ ],
+)
+
+cc_binary(
+ name = "verity_randread_benchmark",
+ testonly = 1,
+ srcs = [
+ "verity_randread_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:capability_util",
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ ],
+)
+
+cc_binary(
+ name = "verity_open_read_close_benchmark",
+ testonly = 1,
+ srcs = [
+ "verity_open_read_close_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:capability_util",
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ ],
+)
+
+cc_binary(
+ name = "verity_stat_benchmark",
+ testonly = 1,
+ srcs = [
+ "verity_stat_benchmark.cc",
+ ],
+ deps = [
+ gbenchmark,
+ gtest,
+ "//test/util:capability_util",
+ "//test/util:fs_util",
+ "//test/util:logging",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ "@com_google_absl//absl/strings",
+ ],
+)
diff --git a/test/perf/linux/dup_benchmark.cc b/test/perf/linux/dup_benchmark.cc
new file mode 100644
index 000000000..5d808d225
--- /dev/null
+++ b/test/perf/linux/dup_benchmark.cc
@@ -0,0 +1,55 @@
+// 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 <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
+#include "test/util/fs_util.h"
+#include "test/util/logging.h"
+#include "test/util/temp_path.h"
+
+namespace gvisor {
+
+namespace {
+
+void BM_Dup(benchmark::State& state) {
+ const int size = state.range(0);
+
+ for (auto _ : state) {
+ std::vector<int> v;
+ for (int i = 0; i < size; i++) {
+ int fd = dup(2);
+ TEST_CHECK(fd != -1);
+ v.push_back(fd);
+ }
+ for (int i = 0; i < size; i++) {
+ int fd = v[i];
+ close(fd);
+ }
+ }
+ state.SetItemsProcessed(state.iterations() * size);
+}
+
+BENCHMARK(BM_Dup)->Range(1, 1 << 15)->UseRealTime();
+
+} // namespace
+
+} // namespace gvisor
diff --git a/test/perf/linux/randread_benchmark.cc b/test/perf/linux/randread_benchmark.cc
index b0eb8c24e..11b56a8cb 100644
--- a/test/perf/linux/randread_benchmark.cc
+++ b/test/perf/linux/randread_benchmark.cc
@@ -85,7 +85,7 @@ void BM_RandRead(benchmark::State& state) {
unsigned int seed = 1;
for (auto _ : state) {
TEST_CHECK(PreadFd(fd.get(), buf.data(), buf.size(),
- rand_r(&seed) % kFileSize) == size);
+ rand_r(&seed) % (kFileSize - buf.size())) == size);
}
state.SetBytesProcessed(static_cast<int64_t>(size) *
diff --git a/test/perf/linux/send_recv_benchmark.cc b/test/perf/linux/send_recv_benchmark.cc
index d73e49523..2d443f54f 100644
--- a/test/perf/linux/send_recv_benchmark.cc
+++ b/test/perf/linux/send_recv_benchmark.cc
@@ -23,10 +23,10 @@
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
#include "benchmark/benchmark.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/logging.h"
#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -80,6 +80,9 @@ void BM_Recvmsg(benchmark::State& state) {
int64_t bytes_received = 0;
for (auto ignored : state) {
int n = recvmsg(recv_socket.get(), recv_msg.header(), 0);
+ if (n == -1 && errno == EINTR) {
+ continue;
+ }
TEST_CHECK(n > 0);
bytes_received += n;
}
@@ -108,6 +111,9 @@ void BM_Sendmsg(benchmark::State& state) {
int64_t bytes_sent = 0;
for (auto ignored : state) {
int n = sendmsg(send_socket.get(), send_msg.header(), 0);
+ if (n == -1 && errno == EINTR) {
+ continue;
+ }
TEST_CHECK(n > 0);
bytes_sent += n;
}
@@ -137,6 +143,9 @@ void BM_Recvfrom(benchmark::State& state) {
for (auto ignored : state) {
int n = recvfrom(recv_socket.get(), recv_buffer, kMessageSize, 0, nullptr,
nullptr);
+ if (n == -1 && errno == EINTR) {
+ continue;
+ }
TEST_CHECK(n > 0);
bytes_received += n;
}
@@ -166,6 +175,9 @@ void BM_Sendto(benchmark::State& state) {
int64_t bytes_sent = 0;
for (auto ignored : state) {
int n = sendto(send_socket.get(), send_buffer, kMessageSize, 0, nullptr, 0);
+ if (n == -1 && errno == EINTR) {
+ continue;
+ }
TEST_CHECK(n > 0);
bytes_sent += n;
}
@@ -247,6 +259,9 @@ void BM_RecvmsgWithControlBuf(benchmark::State& state) {
int64_t bytes_received = 0;
for (auto ignored : state) {
int n = recvmsg(recv_socket.get(), recv_msg.header(), 0);
+ if (n == -1 && errno == EINTR) {
+ continue;
+ }
TEST_CHECK(n > 0);
bytes_received += n;
}
@@ -316,7 +331,11 @@ void BM_SendmsgTCP(benchmark::State& state) {
ScopedThread t([&recv_msg, &recv_socket, &notification] {
while (!notification.HasBeenNotified()) {
- TEST_CHECK(recvmsg(recv_socket.get(), recv_msg.header(), 0) >= 0);
+ int rc = recvmsg(recv_socket.get(), recv_msg.header(), 0);
+ if (rc == -1 && errno == EINTR) {
+ continue;
+ }
+ TEST_CHECK(rc >= 0);
}
});
diff --git a/test/perf/linux/verity_open_benchmark.cc b/test/perf/linux/verity_open_benchmark.cc
new file mode 100644
index 000000000..026b6f101
--- /dev/null
+++ b/test/perf/linux/verity_open_benchmark.cc
@@ -0,0 +1,71 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
+#include "test/util/capability_util.h"
+#include "test/util/fs_util.h"
+#include "test/util/logging.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+void BM_Open(benchmark::State& state) {
+ const int size = state.range(0);
+ std::vector<TempPath> cache;
+ std::vector<EnableTarget> targets;
+
+ // Mount a tmpfs file system to be wrapped by a verity fs.
+ TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ TEST_CHECK(mount("", dir.path().c_str(), "tmpfs", 0, "") == 0);
+
+ for (int i = 0; i < size; i++) {
+ auto path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));
+ targets.emplace_back(
+ EnableTarget(std::string(Basename(path.path())), O_RDONLY));
+ cache.emplace_back(std::move(path));
+ }
+
+ std::string verity_dir =
+ TEST_CHECK_NO_ERRNO_AND_VALUE(MountVerity(dir.path(), targets));
+
+ unsigned int seed = 1;
+ for (auto _ : state) {
+ const int chosen = rand_r(&seed) % size;
+ int fd = open(JoinPath(verity_dir, targets[chosen].path).c_str(), O_RDONLY);
+ TEST_CHECK(fd != -1);
+ close(fd);
+ }
+}
+
+BENCHMARK(BM_Open)->Range(1, 128)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/perf/linux/verity_open_read_close_benchmark.cc b/test/perf/linux/verity_open_read_close_benchmark.cc
new file mode 100644
index 000000000..e77577f22
--- /dev/null
+++ b/test/perf/linux/verity_open_read_close_benchmark.cc
@@ -0,0 +1,75 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
+#include "test/util/capability_util.h"
+#include "test/util/fs_util.h"
+#include "test/util/logging.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+void BM_VerityOpenReadClose(benchmark::State& state) {
+ const int size = state.range(0);
+
+ // Mount a tmpfs file system to be wrapped by a verity fs.
+ TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ TEST_CHECK(mount("", dir.path().c_str(), "tmpfs", 0, "") == 0);
+
+ std::vector<TempPath> cache;
+ std::vector<EnableTarget> targets;
+
+ for (int i = 0; i < size; i++) {
+ auto file = ASSERT_NO_ERRNO_AND_VALUE(
+ TempPath::CreateFileWith(dir.path(), "some contents", 0644));
+ targets.emplace_back(
+ EnableTarget(std::string(Basename(file.path())), O_RDONLY));
+ cache.emplace_back(std::move(file));
+ }
+
+ std::string verity_dir =
+ TEST_CHECK_NO_ERRNO_AND_VALUE(MountVerity(dir.path(), targets));
+
+ char buf[1];
+ unsigned int seed = 1;
+ for (auto _ : state) {
+ const int chosen = rand_r(&seed) % size;
+ int fd = open(JoinPath(verity_dir, targets[chosen].path).c_str(), O_RDONLY);
+ TEST_CHECK(fd != -1);
+ TEST_CHECK(read(fd, buf, 1) == 1);
+ close(fd);
+ }
+}
+
+BENCHMARK(BM_VerityOpenReadClose)->Range(1000, 16384)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/perf/linux/verity_randread_benchmark.cc b/test/perf/linux/verity_randread_benchmark.cc
new file mode 100644
index 000000000..4178cfad8
--- /dev/null
+++ b/test/perf/linux/verity_randread_benchmark.cc
@@ -0,0 +1,108 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
+#include "test/util/logging.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+// Create a 1GB file that will be read from at random positions. This should
+// invalid any performance gains from caching.
+const uint64_t kFileSize = Megabytes(1024);
+
+// How many bytes to write at once to initialize the file used to read from.
+const uint32_t kWriteSize = 65536;
+
+// Largest benchmarked read unit.
+const uint32_t kMaxRead = Megabytes(64);
+
+// Global test state, initialized once per process lifetime.
+struct GlobalState {
+ explicit GlobalState() {
+ // Mount a tmpfs file system to be wrapped by a verity fs.
+ tmp_dir_ = TempPath::CreateDir().ValueOrDie();
+ TEST_CHECK(mount("", tmp_dir_.path().c_str(), "tmpfs", 0, "") == 0);
+ file_ = TempPath::CreateFileIn(tmp_dir_.path()).ValueOrDie();
+ filename_ = std::string(Basename(file_.path()));
+
+ FileDescriptor fd = Open(file_.path(), O_WRONLY).ValueOrDie();
+
+ // Try to minimize syscalls by using maximum size writev() requests.
+ std::vector<char> buffer(kWriteSize);
+ RandomizeBuffer(buffer.data(), buffer.size());
+ const std::vector<std::vector<struct iovec>> iovecs_list =
+ GenerateIovecs(kFileSize + kMaxRead, buffer.data(), buffer.size());
+ for (const auto& iovecs : iovecs_list) {
+ TEST_CHECK(writev(fd.get(), iovecs.data(), iovecs.size()) >= 0);
+ }
+ verity_dir_ =
+ MountVerity(tmp_dir_.path(), {EnableTarget(filename_, O_RDONLY)})
+ .ValueOrDie();
+ }
+ TempPath tmp_dir_;
+ TempPath file_;
+ std::string verity_dir_;
+ std::string filename_;
+};
+
+GlobalState& GetGlobalState() {
+ // This gets created only once throughout the lifetime of the process.
+ // Use a dynamically allocated object (that is never deleted) to avoid order
+ // of destruction of static storage variables issues.
+ static GlobalState* const state =
+ // The actual file size is the maximum random seek range (kFileSize) + the
+ // maximum read size so we can read that number of bytes at the end of the
+ // file.
+ new GlobalState();
+ return *state;
+}
+
+void BM_VerityRandRead(benchmark::State& state) {
+ const int size = state.range(0);
+
+ GlobalState& global_state = GetGlobalState();
+ FileDescriptor verity_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(
+ JoinPath(global_state.verity_dir_, global_state.filename_), O_RDONLY));
+ std::vector<char> buf(size);
+
+ unsigned int seed = 1;
+ for (auto _ : state) {
+ TEST_CHECK(PreadFd(verity_fd.get(), buf.data(), buf.size(),
+ rand_r(&seed) % kFileSize) == size);
+ }
+
+ state.SetBytesProcessed(static_cast<int64_t>(size) *
+ static_cast<int64_t>(state.iterations()));
+}
+
+BENCHMARK(BM_VerityRandRead)->Range(1, kMaxRead)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/perf/linux/verity_read_benchmark.cc b/test/perf/linux/verity_read_benchmark.cc
new file mode 100644
index 000000000..738b5ba45
--- /dev/null
+++ b/test/perf/linux/verity_read_benchmark.cc
@@ -0,0 +1,69 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "benchmark/benchmark.h"
+#include "test/util/capability_util.h"
+#include "test/util/fs_util.h"
+#include "test/util/logging.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+void BM_VerityRead(benchmark::State& state) {
+ const int size = state.range(0);
+ const std::string contents(size, 0);
+
+ // Mount a tmpfs file system to be wrapped by a verity fs.
+ TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ TEST_CHECK(mount("", dir.path().c_str(), "tmpfs", 0, "") == 0);
+
+ auto path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
+ dir.path(), contents, TempPath::kDefaultFileMode));
+ std::string filename = std::string(Basename(path.path()));
+
+ std::string verity_dir = TEST_CHECK_NO_ERRNO_AND_VALUE(
+ MountVerity(dir.path(), {EnableTarget(filename, O_RDONLY)}));
+
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(JoinPath(verity_dir, filename), O_RDONLY));
+ std::vector<char> buf(size);
+ for (auto _ : state) {
+ TEST_CHECK(PreadFd(fd.get(), buf.data(), buf.size(), 0) == size);
+ }
+
+ state.SetBytesProcessed(static_cast<int64_t>(size) *
+ static_cast<int64_t>(state.iterations()));
+}
+
+BENCHMARK(BM_VerityRead)->Range(1, 1 << 26)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/perf/linux/verity_stat_benchmark.cc b/test/perf/linux/verity_stat_benchmark.cc
new file mode 100644
index 000000000..b43a9e266
--- /dev/null
+++ b/test/perf/linux/verity_stat_benchmark.cc
@@ -0,0 +1,84 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "absl/strings/str_cat.h"
+#include "benchmark/benchmark.h"
+#include "test/util/fs_util.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+// Creates a file in a nested directory hierarchy at least `depth` directories
+// deep, and stats that file multiple times.
+void BM_VerityStat(benchmark::State& state) {
+ // Create nested directories with given depth.
+ int depth = state.range(0);
+
+ // Mount a tmpfs file system to be wrapped by a verity fs.
+ TempPath top_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ TEST_CHECK(mount("", top_dir.path().c_str(), "tmpfs", 0, "") == 0);
+ std::string dir_path = top_dir.path();
+ std::string child_path = "";
+ std::vector<EnableTarget> targets;
+
+ while (depth-- > 0) {
+ // Don't use TempPath because it will make paths too long to use.
+ //
+ // The top_dir destructor will clean up this whole tree.
+ dir_path = JoinPath(dir_path, absl::StrCat(depth));
+ ASSERT_NO_ERRNO(Mkdir(dir_path, 0755));
+ child_path = JoinPath(child_path, Basename(dir_path));
+ targets.emplace_back(EnableTarget(child_path, O_RDONLY));
+ }
+
+ // Create the file that will be stat'd.
+ const TempPath file =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir_path));
+
+ targets.emplace_back(
+ EnableTarget(JoinPath(child_path, Basename(file.path())), O_RDONLY));
+
+ // Reverse the targets because verity should be enabled from the lowest level.
+ std::reverse(targets.begin(), targets.end());
+
+ std::string verity_dir =
+ TEST_CHECK_NO_ERRNO_AND_VALUE(MountVerity(top_dir.path(), targets));
+
+ struct stat st;
+ for (auto _ : state) {
+ ASSERT_THAT(stat(JoinPath(verity_dir, targets[0].path).c_str(), &st),
+ SyscallSucceeds());
+ }
+}
+
+BENCHMARK(BM_VerityStat)->Range(1, 100)->UseRealTime();
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/root/chroot_test.go b/test/root/chroot_test.go
index 58fcd6f08..5114a9602 100644
--- a/test/root/chroot_test.go
+++ b/test/root/chroot_test.go
@@ -68,13 +68,15 @@ func TestChroot(t *testing.T) {
if err != nil {
t.Fatalf("error listing %q: %v", chroot, err)
}
- if want, got := 1, len(fi); want != got {
+ if want, got := 2, len(fi); want != got {
t.Fatalf("chroot dir got %d entries, want %d", got, want)
}
- // chroot dir is prepared by runsc and should contains only /proc.
- if fi[0].Name() != "proc" {
- t.Errorf("chroot got children %v, want %v", fi[0].Name(), "proc")
+ // chroot dir is prepared by runsc and should contains only /etc and /proc.
+ for i, want := range []string{"etc", "proc"} {
+ if got := fi[i].Name(); got != want {
+ t.Errorf("chroot got child %v, want %v", got, want)
+ }
}
d.CleanUp(ctx)
diff --git a/test/runner/BUILD b/test/runner/BUILD
index f9f788726..2d93aa6af 100644
--- a/test/runner/BUILD
+++ b/test/runner/BUILD
@@ -8,6 +8,7 @@ go_binary(
srcs = ["main.go"],
data = [
"//runsc",
+ "//test/runner/setup_container",
],
visibility = ["//:sandbox"],
deps = [
diff --git a/test/runner/defs.bzl b/test/runner/defs.bzl
index 416f51935..05c75b130 100644
--- a/test/runner/defs.bzl
+++ b/test/runner/defs.bzl
@@ -103,6 +103,8 @@ def _syscall_test(
if platform == "native":
tags.append("nogotsan")
+ container = "container" in tags
+
runner_args = [
# Arguments are passed directly to runner binary.
"--platform=" + platform,
@@ -115,6 +117,7 @@ def _syscall_test(
"--fuse=" + str(fuse),
"--strace=" + str(debug),
"--debug=" + str(debug),
+ "--container=" + str(container),
]
# Call the rule above.
@@ -132,6 +135,7 @@ def syscall_test(
add_overlay = False,
add_uds_tree = False,
add_hostinet = False,
+ vfs1 = True,
vfs2 = True,
fuse = False,
debug = True,
@@ -145,6 +149,7 @@ def syscall_test(
add_overlay: add an overlay test.
add_uds_tree: add a UDS test.
add_hostinet: add a hostinet test.
+ vfs1: enable VFS1 tests. Could be false only if vfs2 is true.
vfs2: enable VFS2 support.
fuse: enable FUSE support.
debug: enable debug output.
@@ -154,7 +159,7 @@ def syscall_test(
if not tags:
tags = []
- if vfs2 and not fuse:
+ if vfs2 and vfs1 and not fuse:
# Generate a vfs1 plain test. Most testing will now be
# biased towards vfs2, with only a single vfs1 case.
_syscall_test(
@@ -168,7 +173,7 @@ def syscall_test(
**kwargs
)
- if not fuse:
+ if vfs1 and not fuse:
# Generate a native test if fuse is not required.
_syscall_test(
test = test,
diff --git a/test/runner/gtest/gtest.go b/test/runner/gtest/gtest.go
index 38e57d62f..affbf1973 100644
--- a/test/runner/gtest/gtest.go
+++ b/test/runner/gtest/gtest.go
@@ -133,17 +133,28 @@ func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]Tes
}
// Run again to extract benchmarks.
- args = append([]string{listBenchmarkFlag}, extraArgs...)
- cmd = exec.Command(testBin, args...)
- out, err = cmd.Output()
+ tb, err := ParseBenchmarks(testBin, extraArgs...)
+ if err != nil {
+ return nil, err
+ }
+ t = append(t, tb...)
+ return t, nil
+}
+
+// ParseBenchmarks returns each benchmark in a third_party/benchmark binary's list as a single test case.
+func ParseBenchmarks(binary string, extraArgs ...string) ([]TestCase, error) {
+ var t []TestCase
+ args := append([]string{listBenchmarkFlag}, extraArgs...)
+ cmd := exec.Command(binary, args...)
+ out, err := cmd.Output()
if err != nil {
// We were able to enumerate tests above, but not benchmarks?
// We requested them, so we return an error in this case.
exitErr, ok := err.(*exec.ExitError)
if !ok {
- return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v", err)
+ return nil, fmt.Errorf("could not enumerate benchmarks: %v", err)
}
- return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
+ return nil, fmt.Errorf("could not enumerate benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
}
benches := strings.Trim(string(out), "\n")
diff --git a/test/runner/main.go b/test/runner/main.go
index 7e8e88ba2..2cab8d2d4 100644
--- a/test/runner/main.go
+++ b/test/runner/main.go
@@ -40,16 +40,18 @@ import (
)
var (
- debug = flag.Bool("debug", false, "enable debug logs")
- strace = flag.Bool("strace", false, "enable strace logs")
- platform = flag.String("platform", "ptrace", "platform to run on")
- network = flag.String("network", "none", "network stack to run on (sandbox, host, none)")
- useTmpfs = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
- fileAccess = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode")
- overlay = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay")
- vfs2 = flag.Bool("vfs2", false, "enable VFS2")
- fuse = flag.Bool("fuse", false, "enable FUSE")
- runscPath = flag.String("runsc", "", "path to runsc binary")
+ debug = flag.Bool("debug", false, "enable debug logs")
+ strace = flag.Bool("strace", false, "enable strace logs")
+ platform = flag.String("platform", "ptrace", "platform to run on")
+ network = flag.String("network", "none", "network stack to run on (sandbox, host, none)")
+ useTmpfs = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
+ fileAccess = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode")
+ overlay = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay")
+ vfs2 = flag.Bool("vfs2", false, "enable VFS2")
+ fuse = flag.Bool("fuse", false, "enable FUSE")
+ container = flag.Bool("container", false, "run tests in their own namespaces (user ns, network ns, etc), pretending to be root")
+ setupContainerPath = flag.String("setup-container", "", "path to setup_container binary (for use with --container)")
+ runscPath = flag.String("runsc", "", "path to runsc binary")
addUDSTree = flag.Bool("add-uds-tree", false, "expose a tree of UDS utilities for use in tests")
// TODO(gvisor.dev/issue/4572): properly support leak checking for runsc, and
@@ -105,6 +107,27 @@ func runTestCaseNative(testBin string, tc gtest.TestCase, t *testing.T) {
cmd.Stderr = os.Stderr
cmd.SysProcAttr = &unix.SysProcAttr{}
+ if *container {
+ // setup_container takes in its target argv as positional arguments.
+ cmd.Path = *setupContainerPath
+ cmd.Args = append([]string{cmd.Path}, cmd.Args...)
+ cmd.SysProcAttr = &unix.SysProcAttr{
+ Cloneflags: unix.CLONE_NEWUSER | unix.CLONE_NEWNET | unix.CLONE_NEWIPC | unix.CLONE_NEWUTS,
+ // Set current user/group as root inside the namespace.
+ UidMappings: []syscall.SysProcIDMap{
+ {ContainerID: 0, HostID: os.Getuid(), Size: 1},
+ },
+ GidMappings: []syscall.SysProcIDMap{
+ {ContainerID: 0, HostID: os.Getgid(), Size: 1},
+ },
+ GidMappingsEnableSetgroups: false,
+ Credential: &syscall.Credential{
+ Uid: 0,
+ Gid: 0,
+ },
+ }
+ }
+
if specutils.HasCapabilities(capability.CAP_SYS_ADMIN) {
cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUTS
}
@@ -147,6 +170,7 @@ func runRunsc(tc gtest.TestCase, spec *specs.Spec) error {
"-network", *network,
"-log-format=text",
"-TESTONLY-unsafe-nonroot=true",
+ "-TESTONLY-allow-packet-endpoint-write=true",
"-net-raw=true",
fmt.Sprintf("-panic-signal=%d", unix.SIGTERM),
"-watchdog-action=panic",
@@ -454,6 +478,13 @@ func main() {
}
*runscPath = specutils.ExePath
}
+ if *container && *setupContainerPath == "" {
+ setupContainer, err := testutil.FindFile("test/runner/setup_container/setup_container")
+ if err != nil {
+ fatalf("cannot find setup_container: %v", err)
+ }
+ *setupContainerPath = setupContainer
+ }
// Make sure stdout and stderr are opened with O_APPEND, otherwise logs
// from outside the sandbox can (and will) stomp on logs from inside
diff --git a/test/runner/setup_container/BUILD b/test/runner/setup_container/BUILD
new file mode 100644
index 000000000..f879ef649
--- /dev/null
+++ b/test/runner/setup_container/BUILD
@@ -0,0 +1,19 @@
+# setup_container contains a shim binary that runs within the test container
+# for syscall tests with container=True.
+
+load("//tools:defs.bzl", "cc_binary")
+
+package(licenses = ["notice"])
+
+cc_binary(
+ name = "setup_container",
+ testonly = 1,
+ srcs = ["setup_container.cc"],
+ visibility = ["//test/runner:__subpackages__"],
+ deps = [
+ "//test/syscalls/linux:socket_netlink_util",
+ "//test/util:capability_util",
+ "//test/util:posix_error",
+ "//test/util:socket_util",
+ ],
+)
diff --git a/test/runner/setup_container/setup_container.cc b/test/runner/setup_container/setup_container.cc
new file mode 100644
index 000000000..e6fae37e1
--- /dev/null
+++ b/test/runner/setup_container/setup_container.cc
@@ -0,0 +1,79 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <linux/capability.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "test/syscalls/linux/socket_netlink_util.h"
+#include "test/util/capability_util.h"
+#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// SetupContainer sets up the networking settings in the current container.
+PosixError SetupContainer() {
+ const PosixErrorOr<bool> have_net_admin = HaveCapability(CAP_NET_ADMIN);
+ if (!have_net_admin.ok()) {
+ std::cerr << "Cannot determine if we have CAP_NET_ADMIN." << std::endl;
+ return have_net_admin.error();
+ }
+ if (have_net_admin.ValueOrDie() && !IsRunningOnGvisor()) {
+ PosixErrorOr<FileDescriptor> sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
+ if (!sockfd.ok()) {
+ std::cerr << "Cannot open socket." << std::endl;
+ return sockfd.error();
+ }
+ int sock = sockfd.ValueOrDie().get();
+ struct ifreq ifr = {};
+ strncpy(ifr.ifr_name, "lo", IFNAMSIZ);
+ if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) {
+ std::cerr << "Cannot get 'lo' flags: " << strerror(errno) << std::endl;
+ return PosixError(errno);
+ }
+ if ((ifr.ifr_flags & IFF_UP) == 0) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) {
+ std::cerr << "Cannot set 'lo' as UP: " << strerror(errno) << std::endl;
+ return PosixError(errno);
+ }
+ }
+ }
+ return NoError();
+}
+
+} // namespace testing
+} // namespace gvisor
+
+using ::gvisor::testing::SetupContainer;
+
+// Binary setup_container initializes the container environment in which tests
+// with container=True will run, then execs the actual test binary.
+// Usage:
+// ./setup_container test_binary [arguments forwarded to test_binary...]
+int main(int argc, char *argv[], char *envp[]) {
+ if (!SetupContainer().ok()) {
+ return 1;
+ }
+ if (argc < 2) {
+ std::cerr << "Must provide arguments to exec." << std::endl;
+ return 2;
+ }
+ if (execve(argv[1], &argv[1], envp) == -1) {
+ std::cerr << "execv returned errno " << errno << std::endl;
+ return 1;
+ }
+}
diff --git a/test/runtimes/runner/lib/lib.go b/test/runtimes/runner/lib/lib.go
index f2db5f9ea..d6b652897 100644
--- a/test/runtimes/runner/lib/lib.go
+++ b/test/runtimes/runner/lib/lib.go
@@ -152,7 +152,7 @@ func getTests(ctx context.Context, d *dockerutil.Container, lang, image string,
return itests, nil
}
-// getBlacklist reads the exclude file and returns a set of test names to
+// getExcludes reads the exclude file and returns a set of test names to
// exclude.
func getExcludes(excludeFile string) (map[string]struct{}, error) {
excludes := make(map[string]struct{})
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 99743b14a..8494862fa 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -331,6 +331,10 @@ syscall_test(
)
syscall_test(
+ test = "//test/syscalls/linux:msgqueue_test",
+)
+
+syscall_test(
size = "medium",
test = "//test/syscalls/linux:msync_test",
)
@@ -355,6 +359,10 @@ syscall_test(
)
syscall_test(
+ test = "//test/syscalls/linux:packet_socket_dgram_test",
+)
+
+syscall_test(
test = "//test/syscalls/linux:packet_socket_raw_test",
)
@@ -648,6 +656,13 @@ syscall_test(
syscall_test(
size = "large",
shard_count = most_shards,
+ tags = ["container"],
+ test = "//test/syscalls/linux:socket_inet_loopback_isolated_test",
+)
+
+syscall_test(
+ size = "large",
+ shard_count = most_shards,
# Takes too long for TSAN. Creates a lot of TCP sockets.
tags = ["nogotsan"],
test = "//test/syscalls/linux:socket_inet_loopback_nogotsan_test",
@@ -696,6 +711,11 @@ syscall_test(
syscall_test(
size = "medium",
+ test = "//test/syscalls/linux:socket_ipv4_datagram_based_socket_unbound_loopback_test",
+)
+
+syscall_test(
+ size = "medium",
test = "//test/syscalls/linux:socket_ipv4_udp_unbound_loopback_test",
)
@@ -731,6 +751,7 @@ syscall_test(
)
syscall_test(
+ add_hostinet = True,
test = "//test/syscalls/linux:socket_netdevice_test",
)
@@ -876,6 +897,10 @@ syscall_test(
)
syscall_test(
+ test = "//test/syscalls/linux:verity_symlink_test",
+)
+
+syscall_test(
add_overlay = True,
test = "//test/syscalls/linux:sync_test",
)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index d8b562e9d..5efb3e620 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -1,4 +1,4 @@
-load("//tools:defs.bzl", "cc_binary", "cc_library", "default_net_util", "gbenchmark", "gtest", "select_arch", "select_system")
+load("//tools:defs.bzl", "cc_binary", "cc_library", "gbenchmark", "gtest", "select_arch", "select_system")
package(
default_visibility = ["//:sandbox"],
@@ -7,8 +7,16 @@ package(
exports_files(
[
+ "packet_socket.cc",
+ "packet_socket_dgram.cc",
+ "packet_socket_raw.cc",
+ "raw_socket.cc",
+ "raw_socket_hdrincl.cc",
+ "raw_socket_icmp.cc",
"socket.cc",
"socket_inet_loopback.cc",
+ "socket_inet_loopback_isolated.cc",
+ "socket_inet_loopback_test_params.h",
"socket_ip_loopback_blocking.cc",
"socket_ip_tcp_generic_loopback.cc",
"socket_ip_tcp_loopback.cc",
@@ -19,6 +27,7 @@ exports_files(
"socket_ip_udp_loopback_blocking.cc",
"socket_ip_udp_loopback_nonblock.cc",
"socket_ip_unbound.cc",
+ "socket_ipv4_datagram_based_socket_unbound_loopback.cc",
"socket_ipv4_udp_unbound_external_networking_test.cc",
"socket_ipv6_udp_unbound_external_networking_test.cc",
"socket_ipv4_udp_unbound_loopback.cc",
@@ -126,9 +135,9 @@ cc_library(
srcs = ["socket_netlink_util.cc"],
hdrs = ["socket_netlink_util.h"],
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
"//test/util:posix_error",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
],
)
@@ -144,36 +153,12 @@ cc_library(
)
cc_library(
- name = "socket_test_util",
- testonly = 1,
- srcs = [
- "socket_test_util.cc",
- "socket_test_util_impl.cc",
- ],
- hdrs = ["socket_test_util.h"],
- defines = select_system(),
- deps = default_net_util() + [
- gtest,
- "@com_google_absl//absl/memory",
- "@com_google_absl//absl/strings",
- "@com_google_absl//absl/strings:str_format",
- "@com_google_absl//absl/time",
- "@com_google_absl//absl/types:optional",
- "//test/util:file_descriptor",
- "//test/util:posix_error",
- "//test/util:temp_path",
- "//test/util:test_util",
- "//test/util:thread_util",
- ],
-)
-
-cc_library(
name = "unix_domain_socket_test_util",
testonly = 1,
srcs = ["unix_domain_socket_test_util.cc"],
hdrs = ["unix_domain_socket_test_util.h"],
deps = [
- ":socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
gtest,
"//test/util:test_util",
@@ -186,7 +171,7 @@ cc_library(
srcs = ["ip_socket_test_util.cc"],
hdrs = ["ip_socket_test_util.h"],
deps = [
- ":socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
],
)
@@ -231,9 +216,9 @@ cc_binary(
srcs = ["accept_bind.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -246,9 +231,9 @@ cc_binary(
srcs = ["accept_bind_stream.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -347,8 +332,8 @@ cc_binary(
srcs = ["bind.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -361,7 +346,7 @@ cc_binary(
srcs = ["socket.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:file_descriptor",
"//test/util:temp_umask",
@@ -376,9 +361,9 @@ cc_binary(
srcs = ["socket_capability.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -477,6 +462,7 @@ cc_binary(
"//test/util:cleanup",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "@com_google_absl//absl/cleanup",
"@com_google_absl//absl/strings",
gtest,
"//test/util:logging",
@@ -537,9 +523,9 @@ cc_binary(
srcs = ["connect_external.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -581,6 +567,7 @@ cc_binary(
deps = [
"//test/util:eventfd_util",
"//test/util:file_descriptor",
+ "@com_google_absl//absl/memory",
gtest,
"//test/util:fs_util",
"//test/util:posix_error",
@@ -752,10 +739,10 @@ cc_binary(
linkstatic = 1,
deps = [
":file_base",
- ":socket_test_util",
"//test/util:cleanup",
"//test/util:eventfd_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
gtest,
@@ -798,12 +785,12 @@ cc_binary(
srcs = ["fcntl.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:cleanup",
"//test/util:eventfd_util",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/flags:flag",
"@com_google_absl//absl/memory",
@@ -831,8 +818,8 @@ cc_binary(
],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
gtest,
@@ -1018,9 +1005,9 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
":unix_domain_socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:signal_util",
"//test/util:test_main",
@@ -1062,9 +1049,9 @@ cc_binary(
linkstatic = 1,
deps = [
":iptables_types",
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -1080,9 +1067,9 @@ cc_binary(
linkstatic = 1,
deps = [
":iptables_types",
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -1455,16 +1442,17 @@ cc_binary(
)
cc_binary(
- name = "packet_socket_raw_test",
+ name = "packet_socket_dgram_test",
testonly = 1,
- srcs = ["packet_socket_raw.cc"],
- defines = select_system(),
+ srcs = ["packet_socket_dgram.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
+ ":ip_socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
+ "//test/util:cleanup",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:endian",
gtest,
@@ -1474,15 +1462,17 @@ cc_binary(
)
cc_binary(
- name = "packet_socket_test",
+ name = "packet_socket_raw_test",
testonly = 1,
- srcs = ["packet_socket.cc"],
+ srcs = ["packet_socket_raw.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
+ ":ip_socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
+ "//test/util:cleanup",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:endian",
gtest,
@@ -1492,6 +1482,21 @@ cc_binary(
)
cc_binary(
+ name = "packet_socket_test",
+ testonly = 1,
+ srcs = ["packet_socket.cc"],
+ linkstatic = 1,
+ deps = [
+ ":ip_socket_test_util",
+ "//test/util:capability_util",
+ "//test/util:file_descriptor",
+ "//test/util:socket_util",
+ gtest,
+ "//test/util:test_main",
+ ],
+)
+
+cc_binary(
name = "pty_test",
testonly = 1,
srcs = ["pty.cc"],
@@ -1538,9 +1543,9 @@ cc_binary(
srcs = select_system(linux = ["partial_bad_buffer.cc"]),
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:posix_error",
@@ -1573,8 +1578,8 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional",
@@ -1771,6 +1776,7 @@ cc_binary(
"//test/util:mount_util",
"@com_google_absl//absl/container:node_hash_set",
"@com_google_absl//absl/strings",
+ "@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
gtest,
@@ -1792,10 +1798,10 @@ cc_binary(
srcs = ["proc_net.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
gtest,
@@ -1883,6 +1889,7 @@ cc_binary(
linkstatic = 1,
deps = [
"@com_google_absl//absl/flags:flag",
+ "@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
gtest,
"//test/util:capability_util",
@@ -1938,10 +1945,10 @@ cc_binary(
srcs = ["raw_socket_hdrincl.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/base:endian",
gtest,
@@ -1957,10 +1964,10 @@ cc_binary(
defines = select_system(),
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
gtest,
"//test/util:test_main",
@@ -1974,10 +1981,10 @@ cc_binary(
srcs = ["raw_socket_icmp.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
"//test/util:capability_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
gtest,
"//test/util:test_main",
@@ -2007,8 +2014,8 @@ cc_binary(
srcs = ["readahead.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:temp_path",
"//test/util:test_main",
@@ -2207,8 +2214,8 @@ cc_binary(
srcs = ["sendfile_socket.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
gtest,
":ip_socket_test_util",
@@ -2386,11 +2393,12 @@ cc_library(
"socket_generic.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
gtest,
+ "//test/util:capability_util",
"//test/util:test_util",
],
alwayslink = 1,
@@ -2406,8 +2414,8 @@ cc_binary(
deps = [
gtest,
":ip_socket_test_util",
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
@@ -2422,8 +2430,8 @@ cc_library(
srcs = ["socket_unix_dgram.cc"],
hdrs = ["socket_unix_dgram.h"],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
@@ -2437,8 +2445,8 @@ cc_library(
srcs = ["socket_unix_seqpacket.cc"],
hdrs = ["socket_unix_seqpacket.h"],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
@@ -2456,7 +2464,7 @@ cc_library(
"socket_ip_tcp_generic.h",
],
deps = [
- ":socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/time",
gtest,
@@ -2477,8 +2485,8 @@ cc_library(
"socket_non_blocking.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_util",
],
@@ -2495,8 +2503,8 @@ cc_library(
"socket_unix_non_stream.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:memory_util",
"//test/util:test_util",
@@ -2515,8 +2523,8 @@ cc_library(
],
deps = [
":ip_socket_test_util",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_util",
],
@@ -2534,7 +2542,7 @@ cc_library(
],
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_util",
],
@@ -2542,6 +2550,24 @@ cc_library(
)
cc_library(
+ name = "socket_ipv4_datagram_based_socket_unbound_test_cases",
+ testonly = 1,
+ srcs = [
+ "socket_ipv4_datagram_based_socket_unbound.cc",
+ ],
+ hdrs = [
+ "socket_ipv4_datagram_based_socket_unbound.h",
+ ],
+ deps = [
+ ":ip_socket_test_util",
+ "//test/util:capability_util",
+ "//test/util:socket_util",
+ gtest,
+ ],
+ alwayslink = 1,
+)
+
+cc_library(
name = "socket_ipv4_udp_unbound_test_cases",
testonly = 1,
srcs = [
@@ -2552,12 +2578,9 @@ cc_library(
],
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/memory",
gtest,
- "//test/util:posix_error",
- "//test/util:save_util",
- "//test/util:test_util",
],
alwayslink = 1,
)
@@ -2573,7 +2596,7 @@ cc_library(
],
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/memory",
gtest,
"//test/util:posix_error",
@@ -2594,9 +2617,9 @@ cc_library(
],
deps = [
":socket_netlink_route_util",
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:cleanup",
+ "//test/util:socket_util",
gtest,
],
alwayslink = 1,
@@ -2613,8 +2636,8 @@ cc_library(
],
deps = [
":socket_netlink_route_util",
- ":socket_test_util",
"//test/util:capability_util",
+ "//test/util:socket_util",
gtest,
],
alwayslink = 1,
@@ -2631,7 +2654,7 @@ cc_library(
],
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_util",
],
alwayslink = 1,
@@ -2678,10 +2701,10 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_generic_test_cases",
- ":socket_test_util",
":socket_unix_cmsg_test_cases",
":socket_unix_test_cases",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2696,8 +2719,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_non_blocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2710,10 +2733,10 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_non_stream_test_cases",
- ":socket_test_util",
":socket_unix_dgram_test_cases",
":socket_unix_non_stream_test_cases",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2725,8 +2748,8 @@ cc_binary(
srcs = ["socket_unix_dgram_non_blocking.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -2742,10 +2765,10 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_non_stream_test_cases",
- ":socket_test_util",
":socket_unix_non_stream_test_cases",
":socket_unix_seqpacket_test_cases",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2757,8 +2780,8 @@ cc_binary(
srcs = ["socket_unix_stream.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_main",
@@ -2776,7 +2799,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ip_tcp_generic_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2791,7 +2814,7 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -2808,7 +2831,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_generic_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2824,7 +2847,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_non_blocking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2842,7 +2865,7 @@ cc_binary(
":socket_generic_test_cases",
":socket_ip_udp_test_cases",
":socket_non_stream_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2858,7 +2881,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv4_udp_unbound_external_networking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2874,7 +2897,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv6_udp_unbound_external_networking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2890,8 +2913,8 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_bind_to_device_util",
- ":socket_test_util",
"//test/util:capability_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -2909,8 +2932,8 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_bind_to_device_util",
- ":socket_test_util",
"//test/util:capability_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/container:node_hash_map",
gtest,
"//test/util:test_main",
@@ -2929,8 +2952,8 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_bind_to_device_util",
- ":socket_test_util",
"//test/util:capability_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -2948,7 +2971,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_non_blocking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2964,9 +2987,21 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv4_udp_unbound_test_cases",
- ":socket_test_util",
"//test/util:test_main",
- "//test/util:test_util",
+ ],
+)
+
+cc_binary(
+ name = "socket_ipv4_datagram_based_socket_unbound_loopback_test",
+ testonly = 1,
+ srcs = [
+ "socket_ipv4_datagram_based_socket_unbound_loopback.cc",
+ ],
+ linkstatic = 1,
+ deps = [
+ ":ip_socket_test_util",
+ ":socket_ipv4_datagram_based_socket_unbound_test_cases",
+ "//test/util:test_main",
],
)
@@ -2980,7 +3015,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv6_udp_unbound_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -2995,7 +3030,7 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3013,7 +3048,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv4_udp_unbound_netlink_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3029,7 +3064,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_ipv6_udp_unbound_netlink_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3044,7 +3079,7 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3061,8 +3096,8 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_netlink_route_util",
- ":socket_test_util",
"//test/util:capability_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3078,8 +3113,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_generic_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3094,8 +3129,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_non_blocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3110,10 +3145,10 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_generic_test_cases",
- ":socket_test_util",
":socket_unix_cmsg_test_cases",
":socket_unix_test_cases",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3128,13 +3163,23 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_non_blocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
)
+cc_library(
+ name = "socket_inet_loopback_test_params",
+ testonly = 1,
+ hdrs = ["socket_inet_loopback_test_params.h"],
+ deps = [
+ "//test/util:socket_util",
+ gtest,
+ ],
+)
+
cc_binary(
name = "socket_inet_loopback_test",
testonly = 1,
@@ -3142,8 +3187,9 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ ":socket_inet_loopback_test_params",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/memory",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
@@ -3163,16 +3209,31 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
+ ":socket_inet_loopback_test_params",
"//test/util:file_descriptor",
- "@com_google_absl//absl/memory",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
gtest,
"//test/util:posix_error",
"//test/util:save_util",
"//test/util:test_main",
"//test/util:test_util",
- "//test/util:thread_util",
+ ],
+)
+
+cc_binary(
+ name = "socket_inet_loopback_isolated_test",
+ testonly = 1,
+ srcs = ["socket_inet_loopback_isolated.cc"],
+ linkstatic = 1,
+ deps = [
+ ":socket_inet_loopback_test_params",
+ ":socket_netlink_util",
+ "//test/util:socket_util",
+ gtest,
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "@com_google_absl//absl/time",
],
)
@@ -3182,8 +3243,8 @@ cc_binary(
srcs = ["socket_netlink.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3198,10 +3259,10 @@ cc_binary(
deps = [
":socket_netlink_route_util",
":socket_netlink_util",
- ":socket_test_util",
"//test/util:capability_util",
"//test/util:cleanup",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings:str_format",
gtest,
"//test/util:test_main",
@@ -3216,8 +3277,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_netlink_util",
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3236,8 +3297,8 @@ cc_library(
"socket_stream.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
@@ -3255,8 +3316,8 @@ cc_library(
"socket_blocking.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
@@ -3276,10 +3337,13 @@ cc_library(
"socket_unix.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:file_descriptor",
+ "//test/util:memory_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
gtest,
+ "//test/util:temp_path",
"//test/util:test_util",
"//test/util:thread_util",
],
@@ -3296,8 +3360,8 @@ cc_library(
"socket_unix_cmsg.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/strings",
gtest,
"//test/util:test_util",
@@ -3316,8 +3380,8 @@ cc_library(
"socket_stream_blocking.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
@@ -3337,8 +3401,8 @@ cc_library(
"socket_stream_nonblock.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_util",
],
@@ -3355,8 +3419,8 @@ cc_library(
"socket_non_stream_blocking.h",
],
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
@@ -3391,8 +3455,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_stream_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3407,8 +3471,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_stream_blocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3424,7 +3488,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_stream_blocking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3439,8 +3503,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_stream_nonblocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"//test/util:test_main",
"//test/util:test_util",
],
@@ -3452,8 +3516,8 @@ cc_binary(
srcs = ["socket_unix_unbound_dgram.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3466,8 +3530,8 @@ cc_binary(
srcs = ["socket_unix_unbound_abstract.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3480,8 +3544,8 @@ cc_binary(
srcs = ["socket_unix_unbound_filesystem.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:file_descriptor",
"//test/util:test_main",
@@ -3498,8 +3562,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_blocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3516,7 +3580,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_blocking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3532,8 +3596,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_non_stream_blocking_test_cases",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3550,7 +3614,7 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_non_stream_blocking_test_cases",
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3565,10 +3629,10 @@ cc_binary(
],
linkstatic = 1,
deps = [
- ":socket_test_util",
":socket_unix_cmsg_test_cases",
":socket_unix_test_cases",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3581,8 +3645,8 @@ cc_binary(
srcs = ["socket_unix_unbound_seqpacket.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3595,8 +3659,8 @@ cc_binary(
srcs = ["socket_unix_unbound_stream.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -3610,8 +3674,8 @@ cc_binary(
linkstatic = 1,
deps = [
":socket_netlink_util",
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:endian",
gtest,
"//test/util:test_main",
@@ -3693,6 +3757,23 @@ cc_binary(
)
cc_binary(
+ name = "verity_symlink_test",
+ testonly = 1,
+ srcs = ["verity_symlink.cc"],
+ linkstatic = 1,
+ deps = [
+ "//test/util:capability_util",
+ gtest,
+ "//test/util:fs_util",
+ "//test/util:mount_util",
+ "//test/util:temp_path",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ "//test/util:verity_util",
+ ],
+)
+
+cc_binary(
name = "sync_test",
testonly = 1,
# Android does not support syncfs in r22.
@@ -3751,8 +3832,8 @@ cc_binary(
defines = select_system(),
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
"@com_google_absl//absl/time",
gtest,
"//test/util:posix_error",
@@ -3862,7 +3943,7 @@ cc_binary(
srcs = ["tuntap.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
+ "//test/util:socket_util",
gtest,
":socket_netlink_route_util",
"//test/util:capability_util",
@@ -3895,8 +3976,8 @@ cc_binary(
linkstatic = 1,
deps = [
":ip_socket_test_util",
- ":socket_test_util",
":unix_domain_socket_test_util",
+ "//test/util:socket_util",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
@@ -3915,8 +3996,8 @@ cc_binary(
srcs = ["udp_bind.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
"//test/util:file_descriptor",
+ "//test/util:socket_util",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -4096,12 +4177,10 @@ cc_binary(
srcs = ["network_namespace.cc"],
linkstatic = 1,
deps = [
- ":socket_test_util",
gtest,
+ ":ip_socket_test_util",
"//test/util:capability_util",
- "//test/util:posix_error",
"//test/util:test_main",
- "//test/util:test_util",
"//test/util:thread_util",
],
)
@@ -4141,6 +4220,22 @@ cc_binary(
)
cc_binary(
+ name = "msgqueue_test",
+ testonly = 1,
+ srcs = ["msgqueue.cc"],
+ linkstatic = 1,
+ deps = [
+ "//test/util:capability_util",
+ "//test/util:signal_util",
+ "//test/util:temp_path",
+ "//test/util:test_util",
+ "//test/util:thread_util",
+ "@com_google_absl//absl/synchronization",
+ "@com_google_absl//absl/time",
+ ],
+)
+
+cc_binary(
name = "fadvise64_test",
testonly = 1,
srcs = ["fadvise64.cc"],
diff --git a/test/syscalls/linux/accept_bind.cc b/test/syscalls/linux/accept_bind.cc
index fe560cfc5..0d16d1d83 100644
--- a/test/syscalls/linux/accept_bind.cc
+++ b/test/syscalls/linux/accept_bind.cc
@@ -20,9 +20,9 @@
#include <vector>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -37,8 +37,7 @@ TEST_P(AllSocketPairTest, Listen) {
sockets->first_addr_size()),
SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5),
- SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
}
TEST_P(AllSocketPairTest, ListenIncreaseBacklog) {
@@ -48,10 +47,8 @@ TEST_P(AllSocketPairTest, ListenIncreaseBacklog) {
sockets->first_addr_size()),
SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5),
- SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 10),
- SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 10), SyscallSucceeds());
}
TEST_P(AllSocketPairTest, ListenDecreaseBacklog) {
@@ -61,10 +58,8 @@ TEST_P(AllSocketPairTest, ListenDecreaseBacklog) {
sockets->first_addr_size()),
SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 5),
- SyscallSucceeds());
- ASSERT_THAT(listen(sockets->first_fd(), /* backlog = */ 1),
- SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 5), SyscallSucceeds());
+ ASSERT_THAT(listen(sockets->first_fd(), 1), SyscallSucceeds());
}
TEST_P(AllSocketPairTest, ListenBacklogSizes) {
diff --git a/test/syscalls/linux/accept_bind_stream.cc b/test/syscalls/linux/accept_bind_stream.cc
index 4857f160b..5f2b07105 100644
--- a/test/syscalls/linux/accept_bind_stream.cc
+++ b/test/syscalls/linux/accept_bind_stream.cc
@@ -19,9 +19,9 @@
#include <vector>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/bind.cc b/test/syscalls/linux/bind.cc
index 9547c4ab2..8e1d00619 100644
--- a/test/syscalls/linux/bind.cc
+++ b/test/syscalls/linux/bind.cc
@@ -17,8 +17,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/cgroup.cc b/test/syscalls/linux/cgroup.cc
index f29891571..ca23dfeee 100644
--- a/test/syscalls/linux/cgroup.cc
+++ b/test/syscalls/linux/cgroup.cc
@@ -279,6 +279,23 @@ TEST(Cgroup, UnmountRepeated) {
EXPECT_THAT(umount(c.Path().c_str()), SyscallFailsWithErrno(EINVAL));
}
+TEST(Cgroup, Create) {
+ SKIP_IF(!CgroupsAvailable());
+ Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()));
+ Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs(""));
+ ASSERT_NO_ERRNO(c.CreateChild("child1"));
+ EXPECT_TRUE(ASSERT_NO_ERRNO_AND_VALUE(Exists(c.Path())));
+}
+
+TEST(Cgroup, SubcontainerInitiallyEmpty) {
+ SKIP_IF(!CgroupsAvailable());
+ Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()));
+ Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs(""));
+ Cgroup child = ASSERT_NO_ERRNO_AND_VALUE(c.CreateChild("child1"));
+ auto procs = ASSERT_NO_ERRNO_AND_VALUE(child.Procs());
+ EXPECT_TRUE(procs.empty());
+}
+
TEST(MemoryCgroup, MemoryUsageInBytes) {
SKIP_IF(!CgroupsAvailable());
diff --git a/test/syscalls/linux/chroot.cc b/test/syscalls/linux/chroot.cc
index fab79d300..7e4626f03 100644
--- a/test/syscalls/linux/chroot.cc
+++ b/test/syscalls/linux/chroot.cc
@@ -20,16 +20,17 @@
#include <syscall.h>
#include <unistd.h>
+#include <algorithm>
#include <string>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/cleanup/cleanup.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "test/util/capability_util.h"
-#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/logging.h"
@@ -46,13 +47,52 @@ namespace testing {
namespace {
+// Async-signal-safe conversion from integer to string, appending the string
+// (including a terminating NUL) to buf, which is a buffer of size len bytes.
+// Returns the number of bytes written, or 0 if the buffer is too small.
+//
+// Preconditions: 2 <= radix <= 16.
+template <typename T>
+size_t SafeItoa(T val, char* buf, size_t len, int radix) {
+ size_t n = 0;
+#define _WRITE_OR_FAIL(c) \
+ do { \
+ if (len == 0) { \
+ return 0; \
+ } \
+ buf[n] = (c); \
+ n++; \
+ len--; \
+ } while (false)
+ if (val == 0) {
+ _WRITE_OR_FAIL('0');
+ } else {
+ // Write digits in reverse order, then reverse them at the end.
+ bool neg = val < 0;
+ while (val != 0) {
+ // C/C++ define modulo such that the result is negative if exactly one of
+ // the dividend or divisor is negative, so this handles both positive and
+ // negative values.
+ char c = "fedcba9876543210123456789abcdef"[val % radix + 15];
+ _WRITE_OR_FAIL(c);
+ val /= 10;
+ }
+ if (neg) {
+ _WRITE_OR_FAIL('-');
+ }
+ std::reverse(buf, buf + n);
+ }
+ _WRITE_OR_FAIL('\0');
+ return n;
+#undef _WRITE_OR_FAIL
+}
+
TEST(ChrootTest, Success) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
+ auto temp_dir = TempPath::CreateDir().ValueOrDie();
+ const std::string temp_dir_path = temp_dir.path();
- const auto rest = [] {
- auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
- };
+ const auto rest = [&] { TEST_CHECK_SUCCESS(chroot(temp_dir_path.c_str())); };
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
@@ -101,28 +141,34 @@ TEST(ChrootTest, CreatesNewRoot) {
SyscallSucceeds());
auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const std::string new_root_path = new_root.path();
auto file_in_new_root =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(new_root.path()));
+ const std::string file_in_new_root_path = file_in_new_root.path();
const auto rest = [&] {
// chroot into new_root.
- TEST_CHECK_SUCCESS(chroot(new_root.path().c_str()));
+ TEST_CHECK_SUCCESS(chroot(new_root_path.c_str()));
// getcwd should return "(unreachable)" followed by the initial_cwd.
- char cwd[1024];
- TEST_CHECK_SUCCESS(syscall(__NR_getcwd, cwd, sizeof(cwd)));
- std::string expected_cwd = "(unreachable)";
- expected_cwd += initial_cwd;
- TEST_CHECK(strcmp(cwd, expected_cwd.c_str()) == 0);
+ char buf[1024];
+ TEST_CHECK_SUCCESS(syscall(__NR_getcwd, buf, sizeof(buf)));
+ constexpr char kUnreachablePrefix[] = "(unreachable)";
+ TEST_CHECK(
+ strncmp(buf, kUnreachablePrefix, sizeof(kUnreachablePrefix) - 1) == 0);
+ TEST_CHECK(strcmp(buf + sizeof(kUnreachablePrefix) - 1, initial_cwd) == 0);
// Should not be able to stat file by its full path.
struct stat statbuf;
- TEST_CHECK_ERRNO(stat(file_in_new_root.path().c_str(), &statbuf), ENOENT);
+ TEST_CHECK_ERRNO(stat(file_in_new_root_path.c_str(), &statbuf), ENOENT);
// Should be able to stat file at new rooted path.
- auto basename = std::string(Basename(file_in_new_root.path()));
- auto rootedFile = "/" + basename;
- TEST_CHECK_SUCCESS(stat(rootedFile.c_str(), &statbuf));
+ buf[0] = '/';
+ absl::string_view basename = Basename(file_in_new_root_path);
+ TEST_CHECK(basename.length() < (sizeof(buf) - 2));
+ memcpy(buf + 1, basename.data(), basename.length());
+ buf[basename.length() + 1] = '\0';
+ TEST_CHECK_SUCCESS(stat(buf, &statbuf));
// Should be able to stat cwd at '.' even though it's outside root.
TEST_CHECK_SUCCESS(stat(".", &statbuf));
@@ -131,8 +177,8 @@ TEST(ChrootTest, CreatesNewRoot) {
TEST_CHECK_SUCCESS(chdir("/"));
// getcwd should return "/".
- TEST_CHECK_SUCCESS(syscall(__NR_getcwd, cwd, sizeof(cwd)));
- TEST_CHECK_SUCCESS(strcmp(cwd, "/") == 0);
+ TEST_CHECK_SUCCESS(syscall(__NR_getcwd, buf, sizeof(buf)));
+ TEST_CHECK_SUCCESS(strcmp(buf, "/") == 0);
// Statting '.', '..', '/', and '/..' all return the same dev and inode.
struct stat statbuf_dot;
@@ -160,10 +206,11 @@ TEST(ChrootTest, DotDotFromOpenFD) {
auto fd = ASSERT_NO_ERRNO_AND_VALUE(
Open(dir_outside_root.path(), O_RDONLY | O_DIRECTORY));
auto new_root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const std::string new_root_path = new_root.path();
const auto rest = [&] {
// chroot into new_root.
- TEST_CHECK_SUCCESS(chroot(new_root.path().c_str()));
+ TEST_CHECK_SUCCESS(chroot(new_root_path.c_str()));
// openat on fd with path .. will succeed.
int other_fd;
@@ -184,15 +231,18 @@ TEST(ChrootTest, ProcFdLinkResolutionInChroot) {
const TempPath file_outside_chroot =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const std::string file_outside_chroot_path = file_outside_chroot.path();
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(file_outside_chroot.path(), O_RDONLY));
const FileDescriptor proc_fd = ASSERT_NO_ERRNO_AND_VALUE(
Open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+ auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const std::string temp_dir_path = temp_dir.path();
+
const auto rest = [&] {
- auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
+ TEST_CHECK_SUCCESS(chroot(temp_dir_path.c_str()));
// Opening relative to an already open fd to a node outside the chroot
// works.
@@ -201,9 +251,10 @@ TEST(ChrootTest, ProcFdLinkResolutionInChroot) {
// Proc fd symlinks can escape the chroot if the fd the symlink refers to
// refers to an object outside the chroot.
+ char fd_buf[11];
+ TEST_CHECK(SafeItoa(fd.get(), fd_buf, sizeof(fd_buf), 10));
struct stat s = {};
- TEST_CHECK_SUCCESS(
- fstatat(proc_self_fd.get(), absl::StrCat(fd.get()).c_str(), &s, 0));
+ TEST_CHECK_SUCCESS(fstatat(proc_self_fd.get(), fd_buf, &s, 0));
// Try to stat the stdin fd. Internally, this is handled differently from a
// proc fd entry pointing to a file, since stdin is backed by a host fd, and
@@ -223,10 +274,12 @@ TEST(ChrootTest, ProcMemSelfFdsNoEscapeProcOpen) {
const FileDescriptor proc =
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
+ const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const std::string temp_dir_path = temp_dir.path();
+
const auto rest = [&] {
- // Create and enter a chroot directory.
- const auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
+ // Enter the chroot directory.
+ TEST_CHECK_SUCCESS(chroot(temp_dir_path.c_str()));
// Open a file inside the chroot at /foo.
const FileDescriptor foo =
@@ -234,11 +287,15 @@ TEST(ChrootTest, ProcMemSelfFdsNoEscapeProcOpen) {
// Examine /proc/self/fd/{foo_fd} to see if it exposes the fact that we're
// inside a chroot, the path should be /foo and NOT {chroot_dir}/foo.
- const std::string fd_path = absl::StrCat("self/fd/", foo.get());
+ constexpr char kSelfFdRelpath[] = "self/fd/";
+ char path_buf[20];
+ strcpy(path_buf, kSelfFdRelpath); // NOLINT: need async-signal-safety
+ TEST_CHECK(SafeItoa(foo.get(), path_buf + sizeof(kSelfFdRelpath) - 1,
+ sizeof(path_buf) - (sizeof(kSelfFdRelpath) - 1), 10));
char buf[1024] = {};
size_t bytes_read = 0;
- TEST_CHECK_SUCCESS(bytes_read = readlinkat(proc.get(), fd_path.c_str(), buf,
- sizeof(buf) - 1));
+ TEST_CHECK_SUCCESS(
+ bytes_read = readlinkat(proc.get(), path_buf, buf, sizeof(buf) - 1));
// The link should resolve to something.
TEST_CHECK(bytes_read > 0);
@@ -258,10 +315,12 @@ TEST(ChrootTest, ProcMemSelfMapsNoEscapeProcOpen) {
const FileDescriptor proc =
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
+ const auto temp_dir = TEST_CHECK_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const std::string temp_dir_path = temp_dir.path();
+
const auto rest = [&] {
- // Create and enter a chroot directory.
- const auto temp_dir = TEST_CHECK_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- TEST_CHECK_SUCCESS(chroot(temp_dir.path().c_str()));
+ // Enter the chroot directory.
+ TEST_CHECK_SUCCESS(chroot(temp_dir_path.c_str()));
// Open a file inside the chroot at /foo.
const FileDescriptor foo =
@@ -272,9 +331,12 @@ TEST(ChrootTest, ProcMemSelfMapsNoEscapeProcOpen) {
MAP_PRIVATE, foo.get(), 0);
TEST_CHECK_SUCCESS(reinterpret_cast<int64_t>(foo_map));
- // Always unmap.
- auto cleanup_map =
- Cleanup([&] { TEST_CHECK_SUCCESS(munmap(foo_map, kPageSize)); });
+ // Always unmap. Since this function is called between fork() and execve(),
+ // we can't use gvisor::testing::Cleanup, which uses std::function
+ // and thus may heap-allocate (which is async-signal-unsafe); instead, use
+ // absl::Cleanup, which is templated on the callback type.
+ auto cleanup_map = absl::MakeCleanup(
+ [&] { TEST_CHECK_SUCCESS(munmap(foo_map, kPageSize)); });
// Examine /proc/self/maps to be sure that /foo doesn't appear to be
// mapped with the full chroot path.
@@ -289,8 +351,8 @@ TEST(ChrootTest, ProcMemSelfMapsNoEscapeProcOpen) {
TEST_CHECK(bytes_read > 0);
// Finally we want to make sure the maps don't contain the chroot path
- TEST_CHECK(std::string(buf, bytes_read).find(temp_dir.path()) ==
- std::string::npos);
+ TEST_CHECK(
+ !absl::StrContains(absl::string_view(buf, bytes_read), temp_dir_path));
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}
@@ -302,72 +364,72 @@ TEST(ChrootTest, ProcMountsMountinfoNoEscape) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_CHROOT)));
// Create nested tmpfs mounts.
- auto const outer_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- auto const outer_mount = ASSERT_NO_ERRNO_AND_VALUE(
- Mount("none", outer_dir.path(), "tmpfs", 0, "mode=0700", 0));
-
- auto const inner_dir =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outer_dir.path()));
- auto const inner_mount = ASSERT_NO_ERRNO_AND_VALUE(
- Mount("none", inner_dir.path(), "tmpfs", 0, "mode=0700", 0));
+ const auto outer_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const std::string outer_dir_path = outer_dir.path();
+ const auto outer_mount = ASSERT_NO_ERRNO_AND_VALUE(
+ Mount("none", outer_dir_path, "tmpfs", 0, "mode=0700", 0));
+
+ const auto inner_dir =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(outer_dir_path));
+ const std::string inner_dir_path = inner_dir.path();
+ const auto inner_mount = ASSERT_NO_ERRNO_AND_VALUE(
+ Mount("none", inner_dir_path, "tmpfs", 0, "mode=0700", 0));
+ const std::string inner_dir_in_outer_chroot_path =
+ absl::StrCat("/", Basename(inner_dir_path));
+
+ // Filenames that will be checked for mounts, all relative to /proc dir.
+ std::string paths[3] = {"mounts", "self/mounts", "self/mountinfo"};
+
+ for (const std::string& path : paths) {
+ // We should have both inner and outer mounts.
+ const std::string contents =
+ ASSERT_NO_ERRNO_AND_VALUE(GetContents(JoinPath("/proc", path)));
+ EXPECT_THAT(contents,
+ AllOf(HasSubstr(outer_dir_path), HasSubstr(inner_dir_path)));
+ // We better have at least two mounts: the mounts we created plus the
+ // root.
+ std::vector<absl::string_view> submounts =
+ absl::StrSplit(contents, '\n', absl::SkipWhitespace());
+ ASSERT_GT(submounts.size(), 2);
+ }
- const auto rest = [&outer_dir, &inner_dir] {
- // Filenames that will be checked for mounts, all relative to /proc dir.
- std::string paths[3] = {"mounts", "self/mounts", "self/mountinfo"};
-
- for (const std::string& path : paths) {
- // We should have both inner and outer mounts.
- const std::string contents =
- TEST_CHECK_NO_ERRNO_AND_VALUE(GetContents(JoinPath("/proc", path)));
- EXPECT_THAT(contents, AllOf(HasSubstr(outer_dir.path()),
- HasSubstr(inner_dir.path())));
- // We better have at least two mounts: the mounts we created plus the
- // root.
- std::vector<absl::string_view> submounts =
- absl::StrSplit(contents, '\n', absl::SkipWhitespace());
- TEST_CHECK(submounts.size() > 2);
- }
-
- // Get a FD to /proc before we enter the chroot.
- const FileDescriptor proc =
- TEST_CHECK_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
+ // Get a FD to /proc before we enter the chroot.
+ const FileDescriptor proc =
+ ASSERT_NO_ERRNO_AND_VALUE(Open("/proc", O_RDONLY));
+ const auto rest = [&] {
// Chroot to outer mount.
- TEST_CHECK_SUCCESS(chroot(outer_dir.path().c_str()));
+ TEST_CHECK_SUCCESS(chroot(outer_dir_path.c_str()));
+ char buf[8 * 1024];
for (const std::string& path : paths) {
const FileDescriptor proc_file =
TEST_CHECK_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY));
// Only two mounts visible from this chroot: the inner and outer. Both
// paths should be relative to the new chroot.
- const std::string contents =
- TEST_CHECK_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get()));
- EXPECT_THAT(contents,
- AllOf(HasSubstr(absl::StrCat(Basename(inner_dir.path()))),
- Not(HasSubstr(outer_dir.path())),
- Not(HasSubstr(inner_dir.path()))));
- std::vector<absl::string_view> submounts =
- absl::StrSplit(contents, '\n', absl::SkipWhitespace());
- TEST_CHECK(submounts.size() == 2);
+ ssize_t n = ReadFd(proc_file.get(), buf, sizeof(buf));
+ TEST_PCHECK(n >= 0);
+ buf[n] = '\0';
+ TEST_CHECK(absl::StrContains(buf, Basename(inner_dir_path)));
+ TEST_CHECK(!absl::StrContains(buf, outer_dir_path));
+ TEST_CHECK(!absl::StrContains(buf, inner_dir_path));
+ TEST_CHECK(std::count(buf, buf + n, '\n') == 2);
}
// Chroot to inner mount. We must use an absolute path accessible to our
// chroot.
- const std::string inner_dir_basename =
- absl::StrCat("/", Basename(inner_dir.path()));
- TEST_CHECK_SUCCESS(chroot(inner_dir_basename.c_str()));
+ TEST_CHECK_SUCCESS(chroot(inner_dir_in_outer_chroot_path.c_str()));
for (const std::string& path : paths) {
const FileDescriptor proc_file =
TEST_CHECK_NO_ERRNO_AND_VALUE(OpenAt(proc.get(), path, O_RDONLY));
- const std::string contents =
- TEST_CHECK_NO_ERRNO_AND_VALUE(GetContentsFD(proc_file.get()));
// Only the inner mount visible from this chroot.
- std::vector<absl::string_view> submounts =
- absl::StrSplit(contents, '\n', absl::SkipWhitespace());
- TEST_CHECK(submounts.size() == 1);
+ ssize_t n = ReadFd(proc_file.get(), buf, sizeof(buf));
+ TEST_PCHECK(n >= 0);
+ buf[n] = '\0';
+ TEST_CHECK(std::count(buf, buf + n, '\n') == 1);
}
};
EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
diff --git a/test/syscalls/linux/connect_external.cc b/test/syscalls/linux/connect_external.cc
index 1edb50e47..fb2476da4 100644
--- a/test/syscalls/linux/connect_external.cc
+++ b/test/syscalls/linux/connect_external.cc
@@ -22,9 +22,9 @@
#include <tuple>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// This file contains tests specific to connecting to host UDS managed outside
diff --git a/test/syscalls/linux/dup.cc b/test/syscalls/linux/dup.cc
index ba4e13fb9..8f0974f45 100644
--- a/test/syscalls/linux/dup.cc
+++ b/test/syscalls/linux/dup.cc
@@ -13,9 +13,11 @@
// limitations under the License.
#include <fcntl.h>
+#include <sys/resource.h>
#include <unistd.h>
#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
#include "test/util/eventfd_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
@@ -98,6 +100,45 @@ TEST(DupTest, Dup2) {
ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2));
}
+TEST(DupTest, Rlimit) {
+ auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
+
+ struct rlimit rl = {};
+ EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
+
+ ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
+
+ constexpr int kFDLimit = 101;
+ // Create a file descriptor that will be above the limit.
+ FileDescriptor aboveLimitFD =
+ ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, kFDLimit * 2 - 1));
+
+ rl.rlim_cur = kFDLimit;
+ ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
+ ASSERT_THAT(dup3(fd.get(), kFDLimit, 0), SyscallFails());
+
+ std::vector<std::unique_ptr<FileDescriptor>> fds;
+ int prev_fd = fd.get();
+ int used_fds = 0;
+ for (int i = 0; i < kFDLimit; ++i) {
+ int new_fd = dup(fd.get());
+ if (new_fd == -1) {
+ break;
+ }
+ auto f = absl::make_unique<FileDescriptor>(new_fd);
+ EXPECT_LT(new_fd, kFDLimit);
+ EXPECT_GT(new_fd, prev_fd);
+ // Check that all fds in (prev_fd, new_fd) are used.
+ for (int j = prev_fd + 1; j < new_fd; ++j) {
+ if (fcntl(j, F_GETFD) != -1) used_fds++;
+ }
+ prev_fd = new_fd;
+ fds.push_back(std::move(f));
+ }
+ EXPECT_EQ(fds.size() + used_fds, kFDLimit - fd.get() - 1);
+}
+
TEST(DupTest, Dup2SameFD) {
auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc
index 8202d35fa..a2cc59e83 100644
--- a/test/syscalls/linux/eventfd.cc
+++ b/test/syscalls/linux/eventfd.cc
@@ -149,31 +149,6 @@ 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) {
// Waits will time out at 10 seconds.
diff --git a/test/syscalls/linux/exec.cc b/test/syscalls/linux/exec.cc
index 4df429ed8..a0016146a 100644
--- a/test/syscalls/linux/exec.cc
+++ b/test/syscalls/linux/exec.cc
@@ -306,9 +306,6 @@ TEST(ExecTest, InterpreterScriptNoPath) {
TempPath script = ASSERT_NO_ERRNO_AND_VALUE(
TempPath::CreateFileWith(GetAbsoluteTestTmpdir(), "#!\n\n", 0755));
- std::cerr << "path: " << script.path() << std::endl;
- std::cerr << system(absl::StrCat("cat ", script.path()).c_str()) << std::endl;
-
int execve_errno;
ASSERT_NO_ERRNO_AND_VALUE(
ForkAndExec(script.path(), {script.path()}, {}, nullptr, &execve_errno));
diff --git a/test/syscalls/linux/fallocate.cc b/test/syscalls/linux/fallocate.cc
index 5c839447e..5f1b4d5e5 100644
--- a/test/syscalls/linux/fallocate.cc
+++ b/test/syscalls/linux/fallocate.cc
@@ -31,11 +31,11 @@
#include "absl/strings/str_cat.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/file_base.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/cleanup.h"
#include "test/util/eventfd_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc
index 91526572b..0e78a4d4a 100644
--- a/test/syscalls/linux/fcntl.cc
+++ b/test/syscalls/linux/fcntl.cc
@@ -35,7 +35,6 @@
#include "absl/strings/str_cat.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/cleanup.h"
#include "test/util/eventfd_util.h"
@@ -46,6 +45,7 @@
#include "test/util/posix_error.h"
#include "test/util/save_util.h"
#include "test/util/signal_util.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc
index 10dad042f..686b779be 100644
--- a/test/syscalls/linux/flock.cc
+++ b/test/syscalls/linux/flock.cc
@@ -21,8 +21,8 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/file_base.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc
index f6b78989b..e2622232d 100644
--- a/test/syscalls/linux/inotify.cc
+++ b/test/syscalls/linux/inotify.cc
@@ -1849,34 +1849,6 @@ TEST(Inotify, SpliceOnWatchTarget) {
}));
}
-TEST(Inotify, SpliceOnInotifyFD) {
- int pipefds[2];
- ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds());
-
- const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
- const TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- root.path(), "some content", TempPath::kDefaultFileMode));
-
- const FileDescriptor file1_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_RDONLY));
- const int watcher = ASSERT_NO_ERRNO_AND_VALUE(
- InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS));
-
- char buf;
- EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds());
-
- EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr,
- sizeof(struct inotify_event) + 1, SPLICE_F_NONBLOCK),
- SyscallSucceedsWithValue(sizeof(struct inotify_event)));
-
- const FileDescriptor read_fd(pipefds[0]);
- const std::vector<Event> events =
- ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(read_fd.get()));
- ASSERT_THAT(events, Are({Event(IN_ACCESS, watcher)}));
-}
-
// Watches on a parent should not be triggered by actions on a hard link to one
// of its children that has a different parent.
TEST(Inotify, LinkOnOtherParent) {
diff --git a/test/syscalls/linux/ioctl.cc b/test/syscalls/linux/ioctl.cc
index 9b16d1558..88056ef2e 100644
--- a/test/syscalls/linux/ioctl.cc
+++ b/test/syscalls/linux/ioctl.cc
@@ -26,10 +26,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/signal_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/ip6tables.cc b/test/syscalls/linux/ip6tables.cc
index e0e146067..d11b45d4a 100644
--- a/test/syscalls/linux/ip6tables.cc
+++ b/test/syscalls/linux/ip6tables.cc
@@ -17,9 +17,9 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/iptables.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/ip_socket_test_util.cc b/test/syscalls/linux/ip_socket_test_util.cc
index e90a7e411..a1216d23f 100644
--- a/test/syscalls/linux/ip_socket_test_util.cc
+++ b/test/syscalls/linux/ip_socket_test_util.cc
@@ -156,6 +156,14 @@ SocketKind ICMPv6UnboundSocket(int type) {
UnboundSocketCreator(AF_INET6, type | SOCK_DGRAM, IPPROTO_ICMPV6)};
}
+SocketKind IPv4RawUDPUnboundSocket(int type) {
+ std::string description =
+ absl::StrCat(DescribeSocketType(type), "IPv4 Raw UDP socket");
+ return SocketKind{
+ description, AF_INET, type | SOCK_RAW, IPPROTO_UDP,
+ UnboundSocketCreator(AF_INET, type | SOCK_RAW, IPPROTO_UDP)};
+}
+
SocketKind IPv4UDPUnboundSocket(int type) {
std::string description =
absl::StrCat(DescribeSocketType(type), "IPv4 UDP socket");
diff --git a/test/syscalls/linux/ip_socket_test_util.h b/test/syscalls/linux/ip_socket_test_util.h
index bde481f7e..556838356 100644
--- a/test/syscalls/linux/ip_socket_test_util.h
+++ b/test/syscalls/linux/ip_socket_test_util.h
@@ -21,7 +21,7 @@
#include <string>
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
@@ -35,6 +35,9 @@ uint16_t PortFromInetSockaddr(const struct sockaddr* addr);
// InterfaceIndex returns the index of the named interface.
PosixErrorOr<int> InterfaceIndex(std::string name);
+// GetLoopbackIndex returns the index of the loopback interface.
+inline PosixErrorOr<int> GetLoopbackIndex() { return InterfaceIndex("lo"); }
+
// IPv6TCPAcceptBindSocketPair returns a SocketPairKind that represents
// SocketPairs created with bind() and accept() syscalls with AF_INET6 and the
// given type bound to the IPv6 loopback.
@@ -92,6 +95,10 @@ SocketKind ICMPUnboundSocket(int type);
// created with AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6, and the given type.
SocketKind ICMPv6UnboundSocket(int type);
+// IPv4RawUDPUnboundSocket returns a SocketKind that represents a SimpleSocket
+// created with AF_INET, SOCK_RAW, IPPROTO_UDP, and the given type.
+SocketKind IPv4RawUDPUnboundSocket(int type);
+
// IPv4UDPUnboundSocket returns a SocketKind that represents a SimpleSocket
// created with AF_INET, SOCK_DGRAM, IPPROTO_UDP, and the given type.
SocketKind IPv4UDPUnboundSocket(int type);
diff --git a/test/syscalls/linux/itimer.cc b/test/syscalls/linux/itimer.cc
index ac113e6da..9fb04eae6 100644
--- a/test/syscalls/linux/itimer.cc
+++ b/test/syscalls/linux/itimer.cc
@@ -197,9 +197,9 @@ int TestSIGALRMToMainThread() {
// (but don't guarantee it), so we expect to see most samples on the main
// thread.
//
- // The number of SIGALRMs delivered to a worker should not exceed 20%
+ // The number of SIGALRMs delivered to a worker should not exceed 40%
// of the number of total signals expected (this is somewhat arbitrary).
- const int worker_threshold = result.expected_total / 5;
+ const int worker_threshold = result.expected_total / 5 * 2;
//
// Linux only guarantees timers will never expire before the requested time.
@@ -230,7 +230,8 @@ TEST(ItimerTest, DeliversSIGALRMToMainThread) {
// Not required anymore.
kill.Release();
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << status;
+ EXPECT_EQ(WIFEXITED(status) && WEXITSTATUS(status), 0)
+ << WIFEXITED(status) << " " << WEXITSTATUS(status);
}
// Signals are delivered to threads fairly.
diff --git a/test/syscalls/linux/link.cc b/test/syscalls/linux/link.cc
index 4f9ca1a65..8b208f99a 100644
--- a/test/syscalls/linux/link.cc
+++ b/test/syscalls/linux/link.cc
@@ -142,7 +142,8 @@ TEST(LinkTest, OldnameIsEmpty) {
TEST(LinkTest, OldnameDoesNotExist) {
const std::string oldname = NewTempAbsPath();
const std::string newname = NewTempAbsPath();
- EXPECT_THAT(link("", newname.c_str()), SyscallFailsWithErrno(ENOENT));
+ EXPECT_THAT(link(oldname.c_str(), newname.c_str()),
+ SyscallFailsWithErrno(ENOENT));
}
TEST(LinkTest, NewnameCannotExist) {
diff --git a/test/syscalls/linux/lseek.cc b/test/syscalls/linux/lseek.cc
index d4f89527c..dbc21833f 100644
--- a/test/syscalls/linux/lseek.cc
+++ b/test/syscalls/linux/lseek.cc
@@ -121,7 +121,8 @@ TEST(LseekTest, InvalidFD) {
}
TEST(LseekTest, DirCurEnd) {
- const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/tmp", O_RDONLY));
+ const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Open(GetAbsoluteTestTmpdir().c_str(), O_RDONLY));
ASSERT_THAT(lseek(fd.get(), 0, SEEK_CUR), SyscallSucceedsWithValue(0));
}
diff --git a/test/syscalls/linux/memfd.cc b/test/syscalls/linux/memfd.cc
index 4a450742b..dbd1c93ae 100644
--- a/test/syscalls/linux/memfd.cc
+++ b/test/syscalls/linux/memfd.cc
@@ -445,9 +445,10 @@ TEST(MemfdTest, SealsAreInodeLevelProperties) {
// Tmpfs files also support seals, but are created with F_SEAL_SEAL.
TEST(MemfdTest, TmpfsFilesHaveSealSeal) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs("/tmp")));
+ std::string tmpdir = GetAbsoluteTestTmpdir();
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(tmpdir.c_str())));
const TempPath tmpfs_file =
- ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn("/tmp"));
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmpdir.c_str()));
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfs_file.path(), O_RDWR, 0644));
EXPECT_THAT(fcntl(fd.get(), F_GET_SEALS),
diff --git a/test/syscalls/linux/mmap.cc b/test/syscalls/linux/mmap.cc
index 93a6d9cde..bb2a0cb57 100644
--- a/test/syscalls/linux/mmap.cc
+++ b/test/syscalls/linux/mmap.cc
@@ -29,6 +29,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <limits>
#include <vector>
#include "gmock/gmock.h"
@@ -793,6 +794,19 @@ class MMapFileTest : public MMapTest {
ASSERT_THAT(unlink(filename_.c_str()), SyscallSucceeds());
}
+ bool FSSupportsMap() const {
+ bool supported = true;
+ void* ret = mmap(nullptr, 1, PROT_NONE, MAP_PRIVATE, fd_.get(), 0);
+ if (ret == MAP_FAILED && errno != ENODEV) {
+ supported = false;
+ }
+ if (ret != MAP_FAILED) {
+ munmap(ret, 1);
+ }
+
+ return supported;
+ }
+
ssize_t Read(char* buf, size_t count) {
ssize_t len = 0;
do {
@@ -840,12 +854,14 @@ class MMapFileParamTest
// MAP_POPULATE allowed.
// There isn't a good way to verify it actually did anything.
TEST_P(MMapFileParamTest, MapPopulate) {
+ SKIP_IF(!FSSupportsMap());
ASSERT_THAT(Map(0, kPageSize, prot(), flags() | MAP_POPULATE, fd_.get(), 0),
SyscallSucceeds());
}
// MAP_POPULATE on a short file.
TEST_P(MMapFileParamTest, MapPopulateShort) {
+ SKIP_IF(!FSSupportsMap());
ASSERT_THAT(
Map(0, 2 * kPageSize, prot(), flags() | MAP_POPULATE, fd_.get(), 0),
SyscallSucceeds());
@@ -853,6 +869,7 @@ TEST_P(MMapFileParamTest, MapPopulateShort) {
// Read contents from mapped file.
TEST_F(MMapFileTest, Read) {
+ SKIP_IF(!FSSupportsMap());
size_t len = strlen(kFileContents);
ASSERT_EQ(len, Write(kFileContents, len));
@@ -866,6 +883,7 @@ TEST_F(MMapFileTest, Read) {
// Map at an offset.
TEST_F(MMapFileTest, MapOffset) {
+ SKIP_IF(!FSSupportsMap());
ASSERT_THAT(lseek(fd_.get(), kPageSize, SEEK_SET), SyscallSucceeds());
size_t len = strlen(kFileContents);
@@ -881,6 +899,7 @@ TEST_F(MMapFileTest, MapOffset) {
}
TEST_F(MMapFileTest, MapOffsetBeyondEnd) {
+ SKIP_IF(!FSSupportsMap());
SetupGvisorDeathTest();
uintptr_t addr;
@@ -895,16 +914,46 @@ TEST_F(MMapFileTest, MapOffsetBeyondEnd) {
::testing::KilledBySignal(SIGBUS), "");
}
-// Verify mmap fails when sum of length and offset overflows.
-TEST_F(MMapFileTest, MapLengthPlusOffsetOverflows) {
- const size_t length = static_cast<size_t>(-kPageSize);
- const off_t offset = kPageSize;
- ASSERT_THAT(Map(0, length, PROT_READ, MAP_PRIVATE, fd_.get(), offset),
- SyscallFailsWithErrno(ENOMEM));
+TEST_F(MMapFileTest, MapSecondToLastPositivePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(
+ Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
+ (std::numeric_limits<off_t>::max() - kPageSize) & ~(kPageSize - 1)),
+ SyscallSucceeds());
+}
+
+TEST_F(MMapFileTest, MapLastPositivePage) {
+ SKIP_IF(!FSSupportsMap());
+ // For regular files, this should fail due to integer overflow of the end
+ // offset.
+ EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
+ std::numeric_limits<off_t>::max() & ~(kPageSize - 1)),
+ SyscallFailsWithErrno(EOVERFLOW));
+}
+
+TEST_F(MMapFileTest, MapFirstNegativePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
+ std::numeric_limits<off_t>::min()),
+ SyscallFailsWithErrno(EOVERFLOW));
+}
+
+TEST_F(MMapFileTest, MapSecondToLastNegativePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(
+ Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), -(2 * kPageSize)),
+ SyscallFailsWithErrno(EOVERFLOW));
+}
+
+TEST_F(MMapFileTest, MapLastNegativePage) {
+ SKIP_IF(!FSSupportsMap());
+ EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), -kPageSize),
+ SyscallFailsWithErrno(EOVERFLOW));
}
// MAP_PRIVATE PROT_WRITE is allowed on read-only FDs.
TEST_F(MMapFileTest, WritePrivateOnReadOnlyFd) {
+ SKIP_IF(!FSSupportsMap());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY));
@@ -921,6 +970,7 @@ TEST_F(MMapFileTest, WritePrivateOnReadOnlyFd) {
// MAP_SHARED PROT_WRITE not allowed on read-only FDs.
TEST_F(MMapFileTest, WriteSharedOnReadOnlyFd) {
+ SKIP_IF(!FSSupportsMap());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY));
@@ -932,6 +982,7 @@ TEST_F(MMapFileTest, WriteSharedOnReadOnlyFd) {
// Mmap not allowed on O_PATH FDs.
TEST_F(MMapFileTest, MmapFileWithOpath) {
+ SKIP_IF(!FSSupportsMap());
SKIP_IF(IsRunningWithVFS1());
const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const FileDescriptor fd =
@@ -944,6 +995,7 @@ TEST_F(MMapFileTest, MmapFileWithOpath) {
// The FD must be readable.
TEST_P(MMapFileParamTest, WriteOnlyFd) {
+ SKIP_IF(!FSSupportsMap());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY));
@@ -955,6 +1007,7 @@ TEST_P(MMapFileParamTest, WriteOnlyFd) {
// Overwriting the contents of a file mapped MAP_SHARED PROT_READ
// should cause the new data to be reflected in the mapping.
TEST_F(MMapFileTest, ReadSharedConsistentWithOverwrite) {
+ SKIP_IF(!FSSupportsMap());
// Start from scratch.
EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
@@ -994,6 +1047,7 @@ TEST_F(MMapFileTest, ReadSharedConsistentWithOverwrite) {
// Partially overwriting a file mapped MAP_SHARED PROT_READ should be reflected
// in the mapping.
TEST_F(MMapFileTest, ReadSharedConsistentWithPartialOverwrite) {
+ SKIP_IF(!FSSupportsMap());
// Start from scratch.
EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
@@ -1034,6 +1088,7 @@ TEST_F(MMapFileTest, ReadSharedConsistentWithPartialOverwrite) {
// Overwriting a file mapped MAP_SHARED PROT_READ should be reflected in the
// mapping and the file.
TEST_F(MMapFileTest, ReadSharedConsistentWithWriteAndFile) {
+ SKIP_IF(!FSSupportsMap());
// Start from scratch.
EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
@@ -1077,6 +1132,7 @@ TEST_F(MMapFileTest, ReadSharedConsistentWithWriteAndFile) {
// Write data to mapped file.
TEST_F(MMapFileTest, WriteShared) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_.get(), 0),
@@ -1101,6 +1157,7 @@ TEST_F(MMapFileTest, WriteShared) {
// Write data to portion of mapped page beyond the end of the file.
// These writes are not reflected in the file.
TEST_F(MMapFileTest, WriteSharedBeyondEnd) {
+ SKIP_IF(!FSSupportsMap());
// The file is only half of a page. We map an entire page. Writes to the
// end of the mapping must not be reflected in the file.
uintptr_t addr;
@@ -1137,6 +1194,7 @@ TEST_F(MMapFileTest, WriteSharedBeyondEnd) {
// The portion of a mapped page that becomes part of the file after a truncate
// is reflected in the file.
TEST_F(MMapFileTest, WriteSharedTruncateUp) {
+ SKIP_IF(!FSSupportsMap());
// The file is only half of a page. We map an entire page. Writes to the
// end of the mapping must not be reflected in the file.
uintptr_t addr;
@@ -1174,6 +1232,7 @@ TEST_F(MMapFileTest, WriteSharedTruncateUp) {
}
TEST_F(MMapFileTest, ReadSharedTruncateDownThenUp) {
+ SKIP_IF(!FSSupportsMap());
// Start from scratch.
EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
@@ -1213,6 +1272,7 @@ TEST_F(MMapFileTest, ReadSharedTruncateDownThenUp) {
}
TEST_F(MMapFileTest, WriteSharedTruncateDownThenUp) {
+ SKIP_IF(!FSSupportsMap());
// The file is only half of a page. We map an entire page. Writes to the
// end of the mapping must not be reflected in the file.
uintptr_t addr;
@@ -1247,6 +1307,7 @@ TEST_F(MMapFileTest, WriteSharedTruncateDownThenUp) {
}
TEST_F(MMapFileTest, ReadSharedTruncateSIGBUS) {
+ SKIP_IF(!FSSupportsMap());
SetupGvisorDeathTest();
// Start from scratch.
@@ -1277,6 +1338,7 @@ TEST_F(MMapFileTest, ReadSharedTruncateSIGBUS) {
}
TEST_F(MMapFileTest, WriteSharedTruncateSIGBUS) {
+ SKIP_IF(!FSSupportsMap());
SetupGvisorDeathTest();
uintptr_t addr;
@@ -1298,6 +1360,7 @@ TEST_F(MMapFileTest, WriteSharedTruncateSIGBUS) {
}
TEST_F(MMapFileTest, ReadSharedTruncatePartialPage) {
+ SKIP_IF(!FSSupportsMap());
// Start from scratch.
EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
@@ -1327,6 +1390,7 @@ TEST_F(MMapFileTest, ReadSharedTruncatePartialPage) {
// Page can still be accessed and contents are intact after truncating a partial
// page.
TEST_F(MMapFileTest, WriteSharedTruncatePartialPage) {
+ SKIP_IF(!FSSupportsMap());
// Expand the file to a full page.
EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds());
@@ -1354,6 +1418,7 @@ TEST_F(MMapFileTest, WriteSharedTruncatePartialPage) {
// MAP_PRIVATE writes are not carried through to the underlying file.
TEST_F(MMapFileTest, WritePrivate) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
fd_.get(), 0),
@@ -1378,6 +1443,7 @@ TEST_F(MMapFileTest, WritePrivate) {
// SIGBUS raised when reading or writing past end of a mapped file.
TEST_P(MMapFileParamTest, SigBusDeath) {
+ SKIP_IF(!FSSupportsMap());
SetupGvisorDeathTest();
uintptr_t addr;
@@ -1406,6 +1472,7 @@ TEST_P(MMapFileParamTest, SigBusDeath) {
//
// See b/27877699.
TEST_P(MMapFileParamTest, NoSigBusOnPagesBeforeEOF) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0),
SyscallSucceeds());
@@ -1424,6 +1491,7 @@ TEST_P(MMapFileParamTest, NoSigBusOnPagesBeforeEOF) {
// Tests that SIGBUS is not raised when reading or writing from a file-mapped
// page containing EOF, *after* the EOF.
TEST_P(MMapFileParamTest, NoSigBusOnPageContainingEOF) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0),
SyscallSucceeds());
@@ -1446,6 +1514,7 @@ TEST_P(MMapFileParamTest, NoSigBusOnPageContainingEOF) {
// page cache (which does not yet support writing to shared mappings), a bug
// caused reads to fail unnecessarily on such mappings. See b/28913513.
TEST_F(MMapFileTest, ReadingWritableSharedFilePageSucceeds) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
size_t len = strlen(kFileContents);
@@ -1463,6 +1532,7 @@ TEST_F(MMapFileTest, ReadingWritableSharedFilePageSucceeds) {
// read past end of file (resulting in a fault in sentry context in the gVisor
// case). See b/28913513.
TEST_F(MMapFileTest, InternalSigBus) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
fd_.get(), 0),
@@ -1483,6 +1553,7 @@ TEST_F(MMapFileTest, InternalSigBus) {
// /dev/zero to a shared mapping (so that the SIGBUS isn't caught during
// copy-on-write breaking).
TEST_F(MMapFileTest, InternalSigBusZeroing) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_.get(), 0),
@@ -1578,6 +1649,7 @@ TEST_F(MMapTest, NoReserve) {
// Map more than the gVisor page-cache map unit (64k) and ensure that
// it is consistent with reading from the file.
TEST_F(MMapFileTest, Bug38498194) {
+ SKIP_IF(!FSSupportsMap());
// Choose a sufficiently large map unit.
constexpr int kSize = 4 * 1024 * 1024;
EXPECT_THAT(ftruncate(fd_.get(), kSize), SyscallSucceeds());
@@ -1606,6 +1678,7 @@ TEST_F(MMapFileTest, Bug38498194) {
// Tests that reading from a file to a memory mapping of the same file does not
// deadlock. See b/34813270.
TEST_F(MMapFileTest, SelfRead) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
fd_.get(), 0),
@@ -1618,6 +1691,7 @@ TEST_F(MMapFileTest, SelfRead) {
// Tests that writing to a file from a memory mapping of the same file does not
// deadlock. Regression test for b/34813270.
TEST_F(MMapFileTest, SelfWrite) {
+ SKIP_IF(!FSSupportsMap());
uintptr_t addr;
ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
SyscallSucceeds());
@@ -1633,8 +1707,12 @@ TEST(MMapDeathTest, TruncateAfterCOWBreak) {
auto const temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDWR));
ASSERT_THAT(ftruncate(fd.get(), kPageSize), SyscallSucceeds());
- auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
- nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd.get(), 0));
+
+ auto maybe_mapping = Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, fd.get(), 0);
+ // Does FS support mmap?
+ SKIP_IF(maybe_mapping.error().errno_value() == ENODEV);
+ auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(std::move(maybe_mapping));
// Write to this mapping, causing the page to be copied for write.
memset(mapping.ptr(), 'a', mapping.len());
@@ -1661,8 +1739,12 @@ TEST(MMapNoFixtureTest, MapReadOnlyAfterCreateWriteOnly) {
auto const wo_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_WRONLY));
ASSERT_THAT(ftruncate(wo_fd.get(), kPageSize), SyscallSucceeds());
- auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(
- Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, ro_fd.get(), 0));
+ auto maybe_mapping =
+ Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, ro_fd.get(), 0);
+ // Does FS support mmap?
+ SKIP_IF(maybe_mapping.error().errno_value() == ENODEV);
+ auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(std::move(maybe_mapping));
+
std::vector<char> buf(kPageSize);
// The test passes if this survives.
std::copy(static_cast<char*>(mapping.ptr()),
diff --git a/test/syscalls/linux/msgqueue.cc b/test/syscalls/linux/msgqueue.cc
new file mode 100644
index 000000000..c4761eba8
--- /dev/null
+++ b/test/syscalls/linux/msgqueue.cc
@@ -0,0 +1,885 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/types.h>
+
+#include "absl/synchronization/notification.h"
+#include "absl/time/clock.h"
+#include "test/util/capability_util.h"
+#include "test/util/signal_util.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
+
+namespace gvisor {
+namespace testing {
+namespace {
+
+// Source: include/uapi/linux/msg.h
+constexpr int msgMnb = 16384; // Maximum number of bytes in a queue.
+constexpr int msgMni = 32000; // Max number of identifiers.
+constexpr int msgPool =
+ (msgMni * msgMnb / 1024); // Size of buffer pool used to hold message data.
+constexpr int msgMap = msgMnb; // Maximum number of entries in message map.
+constexpr int msgMax = 8192; // Maximum number of bytes in a single message.
+constexpr int msgSsz = 16; // Message segment size.
+constexpr int msgTql = msgMnb; // Maximum number of messages on all queues.
+
+constexpr int kInterruptSignal = SIGALRM;
+
+// Queue is a RAII class used to automatically clean message queues.
+class Queue {
+ public:
+ explicit Queue(int id) : id_(id) {}
+ Queue(const Queue&) = delete;
+ Queue& operator=(const Queue&) = delete;
+
+ Queue(Queue&& other) { id_ = other.release(); }
+
+ ~Queue() {
+ if (id_ >= 0) {
+ EXPECT_THAT(msgctl(id_, IPC_RMID, nullptr), SyscallSucceeds());
+ }
+ }
+
+ int release() {
+ int old = id_;
+ id_ = -1;
+ return old;
+ }
+
+ int get() { return id_; }
+
+ private:
+ int id_ = -1;
+};
+
+PosixErrorOr<Queue> Msgget(key_t key, int flags) {
+ int id = msgget(key, flags);
+ if (id == -1) {
+ return PosixError(errno, absl::StrFormat("msgget(%d, %d)", key, flags));
+ }
+ return Queue(id);
+}
+
+// Default size for messages.
+constexpr size_t msgSize = 50;
+
+// msgbuf is a simple buffer using to send and receive text messages for
+// testing purposes.
+struct msgbuf {
+ int64_t mtype;
+ char mtext[msgSize];
+};
+
+bool operator==(msgbuf& a, msgbuf& b) {
+ for (size_t i = 0; i < msgSize; i++) {
+ if (a.mtext[i] != b.mtext[i]) {
+ return false;
+ }
+ }
+ return a.mtype == b.mtype;
+}
+
+// msgmax represents a buffer for the largest possible single message.
+struct msgmax {
+ int64_t mtype;
+ char mtext[msgMax];
+};
+
+// Test simple creation and retrieval for msgget(2).
+TEST(MsgqueueTest, MsgGet) {
+ const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const key_t key = ftok(keyfile.path().c_str(), 1);
+ ASSERT_THAT(key, SyscallSucceeds());
+
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(key, IPC_CREAT));
+ EXPECT_THAT(msgget(key, 0), SyscallSucceedsWithValue(queue.get()));
+}
+
+// Test simple failure scenarios for msgget(2).
+TEST(MsgqueueTest, MsgGetFail) {
+ const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const key_t key = ftok(keyfile.path().c_str(), 1);
+ ASSERT_THAT(key, SyscallSucceeds());
+
+ EXPECT_THAT(msgget(key, 0), SyscallFailsWithErrno(ENOENT));
+
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(key, IPC_CREAT));
+ EXPECT_THAT(msgget(key, IPC_CREAT | IPC_EXCL), SyscallFailsWithErrno(EEXIST));
+}
+
+// Test using msgget(2) with IPC_PRIVATE option.
+TEST(MsgqueueTest, MsgGetIpcPrivate) {
+ Queue queue1 = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0));
+ Queue queue2 = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0));
+ EXPECT_NE(queue1.get(), queue2.get());
+}
+
+// Test simple msgsnd and msgrcv.
+TEST(MsgqueueTest, MsgOpSimple) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, "A message."};
+ msgbuf rcv;
+
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, 0),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+ EXPECT_TRUE(buf == rcv);
+}
+
+// Test msgsnd and msgrcv of an empty message.
+TEST(MsgqueueTest, MsgOpEmpty) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, ""};
+ msgbuf rcv;
+
+ ASSERT_THAT(msgsnd(queue.get(), &buf, 0, 0), SyscallSucceeds());
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, 0),
+ SyscallSucceedsWithValue(0));
+}
+
+// Test truncation of message with MSG_NOERROR flag.
+TEST(MsgqueueTest, MsgOpTruncate) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, ""};
+ msgbuf rcv;
+
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) - 1, 0, MSG_NOERROR),
+ SyscallSucceedsWithValue(sizeof(buf.mtext) - 1));
+}
+
+// Test msgsnd and msgrcv using invalid arguments.
+TEST(MsgqueueTest, MsgOpInvalidArgs) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, ""};
+
+ EXPECT_THAT(msgsnd(-1, &buf, 0, 0), SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(msgsnd(queue.get(), &buf, -1, 0), SyscallFailsWithErrno(EINVAL));
+
+ buf.mtype = -1;
+ EXPECT_THAT(msgsnd(queue.get(), &buf, 1, 0), SyscallFailsWithErrno(EINVAL));
+
+ EXPECT_THAT(msgrcv(-1, &buf, 1, 0, 0), SyscallFailsWithErrno(EINVAL));
+ EXPECT_THAT(msgrcv(queue.get(), &buf, -1, 0, 0),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+// Test non-blocking msgrcv with an empty queue.
+TEST(MsgqueueTest, MsgOpNoMsg) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf rcv;
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(rcv.mtext) + 1, 0, IPC_NOWAIT),
+ SyscallFailsWithErrno(ENOMSG));
+}
+
+// Test non-blocking msgrcv with a non-empty queue, but no messages of wanted
+// type.
+TEST(MsgqueueTest, MsgOpNoMsgType) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, ""};
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ EXPECT_THAT(msgrcv(queue.get(), &buf, sizeof(buf.mtext) + 1, 2, IPC_NOWAIT),
+ SyscallFailsWithErrno(ENOMSG));
+}
+
+// Test msgrcv with a larger size message than wanted, and truncation disabled.
+TEST(MsgqueueTest, MsgOpTooBig) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, ""};
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ EXPECT_THAT(msgrcv(queue.get(), &buf, sizeof(buf.mtext) - 1, 0, 0),
+ SyscallFailsWithErrno(E2BIG));
+}
+
+// Test receiving messages based on type.
+TEST(MsgqueueTest, MsgRcvType) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ // Send messages in an order and receive them in reverse, based on type,
+ // which shouldn't block.
+ std::map<int64_t, msgbuf> typeToBuf = {
+ {1, msgbuf{1, "Message 1."}}, {2, msgbuf{2, "Message 2."}},
+ {3, msgbuf{3, "Message 3."}}, {4, msgbuf{4, "Message 4."}},
+ {5, msgbuf{5, "Message 5."}}, {6, msgbuf{6, "Message 6."}},
+ {7, msgbuf{7, "Message 7."}}, {8, msgbuf{8, "Message 8."}},
+ {9, msgbuf{9, "Message 9."}}};
+
+ for (auto const& [type, buf] : typeToBuf) {
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+
+ for (int64_t i = typeToBuf.size(); i > 0; i--) {
+ msgbuf rcv;
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(typeToBuf[i].mtext) + 1, i, 0),
+ SyscallSucceedsWithValue(sizeof(typeToBuf[i].mtext)));
+ EXPECT_TRUE(typeToBuf[i] == rcv);
+ }
+}
+
+// Test using MSG_EXCEPT to receive a different-type message.
+TEST(MsgqueueTest, MsgExcept) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ std::map<int64_t, msgbuf> typeToBuf = {
+ {1, msgbuf{1, "Message 1."}},
+ {2, msgbuf{2, "Message 2."}},
+ };
+
+ for (auto const& [type, buf] : typeToBuf) {
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+
+ for (int64_t i = typeToBuf.size(); i > 0; i--) {
+ msgbuf actual = typeToBuf[i == 1 ? 2 : 1];
+ msgbuf rcv;
+
+ EXPECT_THAT(
+ msgrcv(queue.get(), &rcv, sizeof(actual.mtext) + 1, i, MSG_EXCEPT),
+ SyscallSucceedsWithValue(sizeof(actual.mtext)));
+ EXPECT_TRUE(actual == rcv);
+ }
+}
+
+// Test msgrcv with a negative type.
+TEST(MsgqueueTest, MsgRcvTypeNegative) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ // When msgtyp is negative, msgrcv returns the first message with mtype less
+ // than or equal to the absolute value.
+ msgbuf buf{2, "A message."};
+ msgbuf rcv;
+
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ // Nothing is less than or equal to 1.
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, -1, IPC_NOWAIT),
+ SyscallFailsWithErrno(ENOMSG));
+
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, -3, 0),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+ EXPECT_TRUE(buf == rcv);
+}
+
+// Test permission-related failure scenarios.
+TEST(MsgqueueTest, MsgOpPermissions) {
+ AutoCapability cap(CAP_IPC_OWNER, false);
+
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0000));
+
+ msgbuf buf{1, ""};
+
+ EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallFailsWithErrno(EACCES));
+ EXPECT_THAT(msgrcv(queue.get(), &buf, sizeof(buf.mtext), 0, 0),
+ SyscallFailsWithErrno(EACCES));
+}
+
+// Test limits for messages and queues.
+TEST(MsgqueueTest, MsgOpLimits) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ msgbuf buf{1, "A message."};
+
+ // Limit for one message.
+ EXPECT_THAT(msgsnd(queue.get(), &buf, msgMax + 1, 0),
+ SyscallFailsWithErrno(EINVAL));
+
+ // Limit for queue.
+ msgmax limit{1, ""};
+ for (size_t i = 0, msgCount = msgMnb / msgMax; i < msgCount; i++) {
+ EXPECT_THAT(msgsnd(queue.get(), &limit, sizeof(limit.mtext), 0),
+ SyscallSucceeds());
+ }
+ EXPECT_THAT(msgsnd(queue.get(), &limit, sizeof(limit.mtext), IPC_NOWAIT),
+ SyscallFailsWithErrno(EAGAIN));
+}
+
+// MsgCopySupported returns true if MSG_COPY is supported.
+bool MsgCopySupported() {
+ // msgrcv(2) man page states that MSG_COPY flag is available only if the
+ // kernel was built with the CONFIG_CHECKPOINT_RESTORE option. If MSG_COPY
+ // is used when the kernel was configured without the option, msgrcv produces
+ // a ENOSYS error.
+ // To avoid test failure, we perform a small test using msgrcv, and skip the
+ // test if errno == ENOSYS. This means that the test will always run on
+ // gVisor, but may be skipped on native linux.
+
+ auto maybe_id = Msgget(IPC_PRIVATE, 0600);
+ if (!maybe_id.ok()) {
+ return false;
+ }
+ Queue queue(std::move(maybe_id.ValueOrDie()));
+ msgbuf buf{1, "Test message."};
+
+ msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0);
+ return !(msgrcv(queue.get(), &buf, sizeof(buf.mtext) + 1, 0,
+ MSG_COPY | IPC_NOWAIT) == -1 &&
+ errno == ENOSYS);
+}
+
+// Test msgrcv using MSG_COPY.
+TEST(MsgqueueTest, MsgCopy) {
+ SKIP_IF(!MsgCopySupported());
+
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgbuf bufs[5] = {
+ msgbuf{1, "Message 1."}, msgbuf{2, "Message 2."}, msgbuf{3, "Message 3."},
+ msgbuf{4, "Message 4."}, msgbuf{5, "Message 5."},
+ };
+
+ for (auto& buf : bufs) {
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+
+ // Receive a copy of the messages.
+ for (size_t i = 0, size = sizeof(bufs) / sizeof(bufs[0]); i < size; i++) {
+ msgbuf buf = bufs[i];
+ msgbuf rcv;
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, i,
+ MSG_COPY | IPC_NOWAIT),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+ EXPECT_TRUE(buf == rcv);
+ }
+
+ // Re-receive the messages normally.
+ for (auto& buf : bufs) {
+ msgbuf rcv;
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, 0),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+ EXPECT_TRUE(buf == rcv);
+ }
+}
+
+// Test msgrcv using MSG_COPY with invalid arguments.
+TEST(MsgqueueTest, MsgCopyInvalidArgs) {
+ SKIP_IF(!MsgCopySupported());
+
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgbuf rcv;
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, 1, MSG_COPY),
+ SyscallFailsWithErrno(EINVAL));
+
+ EXPECT_THAT(
+ msgrcv(queue.get(), &rcv, msgSize, 5, MSG_COPY | MSG_EXCEPT | IPC_NOWAIT),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+// Test msgrcv using MSG_COPY with invalid indices.
+TEST(MsgqueueTest, MsgCopyInvalidIndex) {
+ SKIP_IF(!MsgCopySupported());
+
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgbuf rcv;
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, -3, MSG_COPY | IPC_NOWAIT),
+ SyscallFailsWithErrno(ENOMSG));
+
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, msgSize, 5, MSG_COPY | IPC_NOWAIT),
+ SyscallFailsWithErrno(ENOMSG));
+}
+
+// Test msgrcv (most probably) blocking on an empty queue.
+TEST(MsgqueueTest, MsgRcvBlocking) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgbuf buf{1, "A message."};
+
+ ScopedThread t([&] {
+ msgbuf rcv;
+ ASSERT_THAT(
+ RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, 0, 0),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+ EXPECT_TRUE(rcv == buf);
+ });
+
+ // Sleep to try and make msgrcv block before sending a message.
+ absl::SleepFor(absl::Milliseconds(150));
+
+ EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+}
+
+// Test msgrcv (most probably) waiting for a specific-type message.
+TEST(MsgqueueTest, MsgRcvTypeBlocking) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgbuf bufs[5] = {{1, "A message."},
+ {1, "A message."},
+ {1, "A message."},
+ {1, "A message."},
+ {2, "A different message."}};
+
+ ScopedThread t([&] {
+ msgbuf buf = bufs[4]; // Buffer that should be received.
+ msgbuf rcv;
+ ASSERT_THAT(
+ RetryEINTR(msgrcv)(queue.get(), &rcv, sizeof(buf.mtext) + 1, 2, 0),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+ EXPECT_TRUE(rcv == buf);
+ });
+
+ // Sleep to try and make msgrcv block before sending messages.
+ absl::SleepFor(absl::Milliseconds(150));
+
+ // Send all buffers in order, only last one should be received.
+ for (auto& buf : bufs) {
+ EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+}
+
+// Test msgsnd (most probably) blocking on a full queue.
+TEST(MsgqueueTest, MsgSndBlocking) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgmax buf{1, ""}; // Has max amount of bytes.
+
+ const size_t msgCount = msgMnb / msgMax; // Number of messages that can be
+ // sent without blocking.
+
+ ScopedThread t([&] {
+ // Fill the queue.
+ for (size_t i = 0; i < msgCount; i++) {
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+
+ // Next msgsnd should block.
+ ASSERT_THAT(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ });
+
+ const DisableSave ds; // Too many syscalls.
+
+ // To increase the chance of the last msgsnd blocking before doing a msgrcv,
+ // we use MSG_COPY option to copy the last index in the queue. As long as
+ // MSG_COPY fails, the queue hasn't yet been filled. When MSG_COPY succeeds,
+ // the queue is filled, and most probably, a blocking msgsnd has been made.
+ msgmax rcv;
+ while (msgrcv(queue.get(), &rcv, msgMax, msgCount - 1,
+ MSG_COPY | IPC_NOWAIT) == -1 &&
+ errno == ENOMSG) {
+ }
+
+ // Delay a bit more for the blocking msgsnd.
+ absl::SleepFor(absl::Milliseconds(100));
+
+ EXPECT_THAT(msgrcv(queue.get(), &rcv, sizeof(buf.mtext), 0, 0),
+ SyscallSucceedsWithValue(sizeof(buf.mtext)));
+}
+
+// Test removing a queue while a blocking msgsnd is executing.
+TEST(MsgqueueTest, MsgSndRmWhileBlocking) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ // Number of messages that can be sent without blocking.
+ const size_t msgCount = msgMnb / msgMax;
+
+ ScopedThread t([&] {
+ // Fill the queue.
+ msgmax buf{1, ""};
+ for (size_t i = 0; i < msgCount; i++) {
+ EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+
+ // Next msgsnd should block. Because we're repeating on EINTR, msgsnd may
+ // race with msgctl(IPC_RMID) and return EINVAL.
+ EXPECT_THAT(RetryEINTR(msgsnd)(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallFails());
+ EXPECT_TRUE((errno == EIDRM || errno == EINVAL));
+ });
+
+ const DisableSave ds; // Too many syscalls.
+
+ // Similar to MsgSndBlocking, we do this to increase the chance of msgsnd
+ // blocking before removing the queue.
+ msgmax rcv;
+ while (msgrcv(queue.get(), &rcv, msgMax, msgCount - 1,
+ MSG_COPY | IPC_NOWAIT) == -1 &&
+ errno == ENOMSG) {
+ }
+ absl::SleepFor(absl::Milliseconds(100));
+
+ EXPECT_THAT(msgctl(queue.release(), IPC_RMID, nullptr), SyscallSucceeds());
+}
+
+// Test removing a queue while a blocking msgrcv is executing.
+TEST(MsgqueueTest, MsgRcvRmWhileBlocking) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ ScopedThread t([&] {
+ // Because we're repeating on EINTR, msgsnd may race with msgctl(IPC_RMID)
+ // and return EINVAL.
+ msgbuf rcv;
+ EXPECT_THAT(RetryEINTR(msgrcv)(queue.get(), &rcv, 1, 2, 0), SyscallFails());
+ EXPECT_TRUE(errno == EIDRM || errno == EINVAL);
+ });
+
+ // Sleep to try and make msgrcv block before sending messages.
+ absl::SleepFor(absl::Milliseconds(150));
+
+ EXPECT_THAT(msgctl(queue.release(), IPC_RMID, nullptr), SyscallSucceeds());
+}
+
+// Test a collection of msgsnd/msgrcv operations in different processes.
+TEST(MsgqueueTest, MsgOpGeneral) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+
+ // Create multiple sending/receiving threads that send messages back and
+ // forth. There's a matching recv for each send, so by the end of the test,
+ // all threads should succeed and return.
+ const std::vector<msgbuf> msgs = {
+ msgbuf{1, "Message 1."}, msgbuf{2, "Message 2."}, msgbuf{3, "Message 3."},
+ msgbuf{4, "Message 4."}, msgbuf{5, "Message 5."}};
+
+ auto receiver = [&](int i) {
+ return [i, &msgs, &queue]() {
+ const msgbuf& target = msgs[i];
+ msgbuf rcv;
+ EXPECT_THAT(RetryEINTR(msgrcv)(queue.get(), &rcv,
+ sizeof(target.mtext) + 1, target.mtype, 0),
+ SyscallSucceedsWithValue(sizeof(target.mtext)));
+ EXPECT_EQ(rcv.mtype, target.mtype);
+ EXPECT_EQ(0, memcmp(rcv.mtext, target.mtext, sizeof(target.mtext)));
+ };
+ };
+
+ ScopedThread r1(receiver(0));
+ ScopedThread r2(receiver(1));
+ ScopedThread r3(receiver(2));
+ ScopedThread r4(receiver(3));
+ ScopedThread r5(receiver(4));
+ ScopedThread r6(receiver(0));
+ ScopedThread r7(receiver(1));
+ ScopedThread r8(receiver(2));
+ ScopedThread r9(receiver(3));
+ ScopedThread r10(receiver(4));
+
+ auto sender = [&](int i) {
+ return [i, &msgs, &queue]() {
+ const msgbuf& target = msgs[i];
+ EXPECT_THAT(
+ RetryEINTR(msgsnd)(queue.get(), &target, sizeof(target.mtext), 0),
+ SyscallSucceeds());
+ };
+ };
+
+ ScopedThread s1(sender(0));
+ ScopedThread s2(sender(1));
+ ScopedThread s3(sender(2));
+ ScopedThread s4(sender(3));
+ ScopedThread s5(sender(4));
+ ScopedThread s6(sender(0));
+ ScopedThread s7(sender(1));
+ ScopedThread s8(sender(2));
+ ScopedThread s9(sender(3));
+ ScopedThread s10(sender(4));
+}
+
+void empty_sighandler(int sig, siginfo_t* info, void* context) {}
+
+TEST(MsgqueueTest, InterruptRecv) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ char buf[64];
+
+ absl::Notification done, exit;
+
+ // Thread calling msgrcv with no corresponding send. It would block forever,
+ // but we'll interrupt with a signal below.
+ ScopedThread t([&] {
+ struct sigaction sa = {};
+ sa.sa_sigaction = empty_sighandler;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ auto cleanup_sigaction =
+ ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kInterruptSignal, sa));
+ auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(
+ ScopedSignalMask(SIG_UNBLOCK, kInterruptSignal));
+
+ EXPECT_THAT(msgrcv(queue.get(), &buf, sizeof(buf), 0, 0),
+ SyscallFailsWithErrno(EINTR));
+
+ done.Notify();
+ exit.WaitForNotification();
+ });
+
+ const DisableSave ds; // Too many syscalls.
+
+ // We want the signal to arrive while msgrcv is blocking, but not after the
+ // thread has exited. Signals that arrive before msgrcv are no-ops.
+ do {
+ EXPECT_THAT(kill(getpid(), kInterruptSignal), SyscallSucceeds());
+ absl::SleepFor(absl::Milliseconds(100)); // Rate limit.
+ } while (!done.HasBeenNotified());
+
+ exit.Notify();
+ t.Join();
+}
+
+TEST(MsgqueueTest, InterruptSend) {
+ Queue queue = ASSERT_NO_ERRNO_AND_VALUE(Msgget(IPC_PRIVATE, 0600));
+ msgmax buf{1, ""};
+ // Number of messages that can be sent without blocking.
+ const size_t msgCount = msgMnb / msgMax;
+
+ // Fill the queue.
+ for (size_t i = 0; i < msgCount; i++) {
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+ }
+
+ absl::Notification done, exit;
+
+ // Thread calling msgsnd on a full queue. It would block forever, but we'll
+ // interrupt with a signal below.
+ ScopedThread t([&] {
+ struct sigaction sa = {};
+ sa.sa_sigaction = empty_sighandler;
+ sigfillset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ auto cleanup_sigaction =
+ ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(kInterruptSignal, sa));
+ auto sa_cleanup = ASSERT_NO_ERRNO_AND_VALUE(
+ ScopedSignalMask(SIG_UNBLOCK, kInterruptSignal));
+
+ EXPECT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallFailsWithErrno(EINTR));
+
+ done.Notify();
+ exit.WaitForNotification();
+ });
+
+ const DisableSave ds; // Too many syscalls.
+
+ // We want the signal to arrive while msgsnd is blocking, but not after the
+ // thread has exited. Signals that arrive before msgsnd are no-ops.
+ do {
+ EXPECT_THAT(kill(getpid(), kInterruptSignal), SyscallSucceeds());
+ absl::SleepFor(absl::Milliseconds(100)); // Rate limit.
+ } while (!done.HasBeenNotified());
+
+ exit.Notify();
+ t.Join();
+}
+
+// Test msgctl with IPC_STAT option.
+TEST(MsgqueueTest, MsgCtlIpcStat) {
+ auto start = absl::Now();
+
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ const uid_t uid = getuid();
+ const gid_t gid = getgid();
+ const pid_t pid = getpid();
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+
+ EXPECT_EQ(ds.msg_perm.__key, IPC_PRIVATE);
+ EXPECT_EQ(ds.msg_perm.uid, uid);
+ EXPECT_EQ(ds.msg_perm.gid, gid);
+ EXPECT_EQ(ds.msg_perm.cuid, uid);
+ EXPECT_EQ(ds.msg_perm.cgid, gid);
+ EXPECT_EQ(ds.msg_perm.mode, 0600);
+
+ EXPECT_EQ(ds.msg_stime, 0);
+ EXPECT_EQ(ds.msg_rtime, 0);
+ EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start));
+
+ EXPECT_EQ(ds.msg_cbytes, 0);
+ EXPECT_EQ(ds.msg_qnum, 0);
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+ EXPECT_EQ(ds.msg_lspid, 0);
+ EXPECT_EQ(ds.msg_lrpid, 0);
+
+ // The timestamps only have a resolution of seconds; slow down so we actually
+ // see the timestamps change.
+ absl::SleepFor(absl::Seconds(1));
+ auto pre_send = absl::Now();
+
+ msgbuf buf{1, "A message."};
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+
+ EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send));
+ EXPECT_EQ(ds.msg_rtime, 0);
+ EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start));
+
+ EXPECT_EQ(ds.msg_cbytes, msgSize);
+ EXPECT_EQ(ds.msg_qnum, 1);
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+ EXPECT_EQ(ds.msg_lspid, pid);
+ EXPECT_EQ(ds.msg_lrpid, 0);
+
+ absl::SleepFor(absl::Seconds(1));
+ auto pre_receive = absl::Now();
+
+ ASSERT_THAT(msgrcv(queue.get(), &buf, sizeof(buf.mtext), 0, 0),
+ SyscallSucceedsWithValue(msgSize));
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+
+ EXPECT_GE(ds.msg_stime, absl::ToTimeT(pre_send));
+ EXPECT_GE(ds.msg_rtime, absl::ToTimeT(pre_receive));
+ EXPECT_GE(ds.msg_ctime, absl::ToTimeT(start));
+
+ EXPECT_EQ(ds.msg_cbytes, 0);
+ EXPECT_EQ(ds.msg_qnum, 0);
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+ EXPECT_EQ(ds.msg_lspid, pid);
+ EXPECT_EQ(ds.msg_lrpid, pid);
+}
+
+// Test msgctl with IPC_STAT option on a write-only queue.
+TEST(MsgqueueTest, MsgCtlIpcStatWriteOnly) {
+ // Drop CAP_IPC_OWNER which allows us to bypass permissions.
+ AutoCapability cap(CAP_IPC_OWNER, false);
+
+ Queue queue(msgget(IPC_PRIVATE, 0200));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds),
+ SyscallFailsWithErrno(EACCES));
+}
+
+// Test msgctl with IPC_SET option.
+TEST(MsgqueueTest, MsgCtlIpcSet) {
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_perm.mode, 0600);
+
+ ds.msg_perm.mode = 0777;
+ ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_perm.mode, 0777);
+}
+
+// Test increasing msg_qbytes beyond limit with IPC_SET.
+TEST(MsgqueueTest, MsgCtlIpcSetMaxBytes) {
+ // Drop CAP_SYS_RESOURCE which allows us to increase msg_qbytes beyond the
+ // system parameter MSGMNB.
+ AutoCapability cap(CAP_SYS_RESOURCE, false);
+
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ struct msqid_ds ds;
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_qbytes, msgMnb);
+
+ ds.msg_qbytes = msgMnb - 10;
+ ASSERT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(queue.get(), IPC_STAT, &ds), SyscallSucceeds());
+ EXPECT_EQ(ds.msg_qbytes, msgMnb - 10);
+
+ ds.msg_qbytes = msgMnb + 10;
+ EXPECT_THAT(msgctl(queue.get(), IPC_SET, &ds), SyscallFailsWithErrno(EPERM));
+}
+
+// Test msgctl with IPC_INFO option.
+TEST(MsgqueueTest, MsgCtlIpcInfo) {
+ struct msginfo info;
+ ASSERT_THAT(msgctl(0, IPC_INFO, reinterpret_cast<struct msqid_ds*>(&info)),
+ SyscallSucceeds());
+
+ EXPECT_GT(info.msgmax, 0);
+ EXPECT_GT(info.msgmni, 0);
+ EXPECT_GT(info.msgmnb, 0);
+ EXPECT_EQ(info.msgpool, msgPool);
+ EXPECT_EQ(info.msgmap, msgMap);
+ EXPECT_EQ(info.msgssz, msgSsz);
+ EXPECT_EQ(info.msgtql, msgTql);
+}
+
+// Test msgctl with MSG_INFO option.
+TEST(MsgqueueTest, MsgCtlMsgInfo) {
+ struct msginfo info;
+ ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast<struct msqid_ds*>(&info)),
+ SyscallSucceeds());
+
+ EXPECT_GT(info.msgmax, 0);
+ EXPECT_GT(info.msgmni, 0);
+ EXPECT_GT(info.msgmnb, 0);
+ EXPECT_EQ(info.msgpool, 0); // Number of queues in the system.
+ EXPECT_EQ(info.msgmap, 0); // Total number of messages in all queues.
+ EXPECT_EQ(info.msgtql, 0); // Total number of bytes in all messages.
+ EXPECT_EQ(info.msgssz, msgSsz);
+
+ // Add a queue and a message.
+ Queue queue(msgget(IPC_PRIVATE, 0600));
+ ASSERT_THAT(queue.get(), SyscallSucceeds());
+
+ msgbuf buf{1, "A message."};
+ ASSERT_THAT(msgsnd(queue.get(), &buf, sizeof(buf.mtext), 0),
+ SyscallSucceeds());
+
+ ASSERT_THAT(msgctl(0, MSG_INFO, reinterpret_cast<struct msqid_ds*>(&info)),
+ SyscallSucceeds());
+
+ EXPECT_GT(info.msgmax, 0);
+ EXPECT_GT(info.msgmni, 0);
+ EXPECT_GT(info.msgmnb, 0);
+ EXPECT_EQ(info.msgpool, 1); // Number of queues in the system.
+ EXPECT_EQ(info.msgmap, 1); // Total number of messages in all queues.
+ EXPECT_EQ(info.msgtql, msgSize); // Total number of bytes in all messages.
+ EXPECT_EQ(info.msgssz, msgSsz);
+}
+
+} // namespace
+} // namespace testing
+} // namespace gvisor
+
+int main(int argc, char** argv) {
+ // Some tests depend on delivering a signal to the main thread. Block the
+ // target signal so that any other threads created by TestInit will also have
+ // the signal blocked.
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, gvisor::testing::kInterruptSignal);
+ TEST_PCHECK(sigprocmask(SIG_BLOCK, &set, nullptr) == 0);
+
+ gvisor::testing::TestInit(&argc, &argv);
+ return gvisor::testing::RunAllTests();
+}
diff --git a/test/syscalls/linux/network_namespace.cc b/test/syscalls/linux/network_namespace.cc
index 133fdecf0..aa9e1bc04 100644
--- a/test/syscalls/linux/network_namespace.cc
+++ b/test/syscalls/linux/network_namespace.cc
@@ -12,18 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <net/if.h>
-#include <sched.h>
-#include <sys/ioctl.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-
-#include "gmock/gmock.h"
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/capability_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
@@ -37,13 +28,7 @@ TEST(NetworkNamespaceTest, LoopbackExists) {
ASSERT_THAT(unshare(CLONE_NEWNET), SyscallSucceedsWithValue(0));
// TODO(gvisor.dev/issue/1833): Update this to test that only "lo" exists.
- // Check loopback device exists.
- int sock = socket(AF_INET, SOCK_DGRAM, 0);
- ASSERT_THAT(sock, SyscallSucceeds());
- struct ifreq ifr;
- strncpy(ifr.ifr_name, "lo", IFNAMSIZ);
- EXPECT_THAT(ioctl(sock, SIOCGIFINDEX, &ifr), SyscallSucceeds())
- << "lo cannot be found";
+ ASSERT_NE(ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()), 0);
});
}
diff --git a/test/syscalls/linux/packet_socket.cc b/test/syscalls/linux/packet_socket.cc
index 861617ff7..43828a52e 100644
--- a/test/syscalls/linux/packet_socket.cc
+++ b/test/syscalls/linux/packet_socket.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The gVisor Authors.
+// Copyright 2021 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,50 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <arpa/inet.h>
-#include <ifaddrs.h>
-#include <linux/capability.h>
-#include <linux/if_arp.h>
-#include <linux/if_packet.h>
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
+#include <net/if.h>
+#include <netinet/if_ether.h>
+#include <netpacket/packet.h>
#include <poll.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <unistd.h>
+
+#include <limits>
#include "gtest/gtest.h"
-#include "absl/base/internal/endian.h"
-#include "test/syscalls/linux/socket_test_util.h"
-#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
-#include "test/util/test_util.h"
-
-// Some of these tests involve sending packets via AF_PACKET sockets and the
-// loopback interface. Because AF_PACKET circumvents so much of the networking
-// stack, Linux sees these packets as "martian", i.e. they claim to be to/from
-// localhost but don't have the usual associated data. Thus Linux drops them by
-// default. You can see where this happens by following the code at:
-//
-// - net/ipv4/ip_input.c:ip_rcv_finish, which calls
-// - net/ipv4/route.c:ip_route_input_noref, which calls
-// - net/ipv4/route.c:ip_route_input_slow, which finds and drops martian
-// packets.
-//
-// To tell Linux not to drop these packets, you need to tell it to accept our
-// funny packets (which are completely valid and correct, but lack associated
-// in-kernel data because we use AF_PACKET):
-//
-// echo 1 >> /proc/sys/net/ipv4/conf/lo/accept_local
-// echo 1 >> /proc/sys/net/ipv4/conf/lo/route_localnet
-//
-// These tests require CAP_NET_RAW to run.
-
-// TODO(gvisor.dev/issue/173): gVisor support.
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
@@ -63,492 +33,191 @@ namespace testing {
namespace {
using ::testing::AnyOf;
+using ::testing::Combine;
using ::testing::Eq;
+using ::testing::Values;
-constexpr char kMessage[] = "soweoneul malhaebwa";
-constexpr in_port_t kPort = 0x409c; // htons(40000)
-
-//
-// "Cooked" tests. Cooked AF_PACKET sockets do not contain link layer
-// headers, and provide link layer destination/source information via a
-// returned struct sockaddr_ll.
-//
-
-// Send kMessage via sock to loopback
-void SendUDPMessage(int sock) {
- struct sockaddr_in dest = {};
- dest.sin_port = kPort;
- dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- dest.sin_family = AF_INET;
- EXPECT_THAT(sendto(sock, kMessage, sizeof(kMessage), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(kMessage)));
-}
-
-// Send an IP packet and make sure ETH_P_<something else> doesn't pick it up.
-TEST(BasicCookedPacketTest, WrongType) {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
- ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, ETH_P_PUP),
- SyscallFailsWithErrno(EPERM));
- GTEST_SKIP();
- }
-
- FileDescriptor sock = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_PUP)));
-
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- SendUDPMessage(udp_sock.get());
-
- // Wait and make sure the socket never becomes readable.
- struct pollfd pfd = {};
- pfd.fd = sock.get();
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
-}
-
-// Tests for "cooked" (SOCK_DGRAM) packet(7) sockets.
-class CookedPacketTest : public ::testing::TestWithParam<int> {
+class PacketSocketCreationTest
+ : public ::testing::TestWithParam<std::tuple<int, int>> {
protected:
- // Creates a socket to be used in tests.
- void SetUp() override;
-
- // Closes the socket created by SetUp().
- void TearDown() override;
-
- // Gets the device index of the loopback device.
- int GetLoopbackIndex();
-
- // The socket used for both reading and writing.
- int socket_;
-};
-
-void CookedPacketTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
- ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
- SyscallFailsWithErrno(EPERM));
- GTEST_SKIP();
- }
-
- if (!IsRunningOnGvisor()) {
- FileDescriptor acceptLocal = ASSERT_NO_ERRNO_AND_VALUE(
- Open("/proc/sys/net/ipv4/conf/lo/accept_local", O_RDONLY));
- FileDescriptor routeLocalnet = ASSERT_NO_ERRNO_AND_VALUE(
- Open("/proc/sys/net/ipv4/conf/lo/route_localnet", O_RDONLY));
- char enabled;
- ASSERT_THAT(read(acceptLocal.get(), &enabled, 1),
- SyscallSucceedsWithValue(1));
- ASSERT_EQ(enabled, '1');
- ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1),
- SyscallSucceedsWithValue(1));
- ASSERT_EQ(enabled, '1');
- }
-
- ASSERT_THAT(socket_ = socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
- SyscallSucceeds());
-}
-
-void CookedPacketTest::TearDown() {
- // TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
- EXPECT_THAT(close(socket_), SyscallSucceeds());
- }
-}
-
-int CookedPacketTest::GetLoopbackIndex() {
- struct ifreq ifr;
- snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
- EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
- return ifr.ifr_ifindex;
-}
-
-// Receive and verify the message via packet socket on interface.
-void ReceiveMessage(int sock, int ifindex) {
- // Wait for the socket to become readable.
- struct pollfd pfd = {};
- pfd.fd = sock;
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1));
-
- // Read and verify the data.
- constexpr size_t packet_size =
- sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
- char buf[64];
- struct sockaddr_ll src = {};
- socklen_t src_len = sizeof(src);
- ASSERT_THAT(recvfrom(sock, buf, sizeof(buf), 0,
- reinterpret_cast<struct sockaddr*>(&src), &src_len),
- SyscallSucceedsWithValue(packet_size));
-
- // sockaddr_ll ends with an 8 byte physical address field, but ethernet
- // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
- // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
- // sizeof(sockaddr_ll).
- ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
-
- // TODO(gvisor.dev/issue/173): Verify protocol once we return it.
- // Verify the source address.
- EXPECT_EQ(src.sll_family, AF_PACKET);
- EXPECT_EQ(src.sll_ifindex, ifindex);
- EXPECT_EQ(src.sll_halen, ETH_ALEN);
- EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
- // This came from the loopback device, so the address is all 0s.
- for (int i = 0; i < src.sll_halen; i++) {
- EXPECT_EQ(src.sll_addr[i], 0);
+ void SetUp() override {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ const auto [type, protocol] = GetParam();
+ ASSERT_THAT(socket(AF_PACKET, type, htons(protocol)),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP() << "Missing packet socket capability";
+ }
}
+};
- // Verify the IP header. We memcpy to deal with pointer aligment.
- struct iphdr ip = {};
- memcpy(&ip, buf, sizeof(ip));
- EXPECT_EQ(ip.ihl, 5);
- EXPECT_EQ(ip.version, 4);
- EXPECT_EQ(ip.tot_len, htons(packet_size));
- EXPECT_EQ(ip.protocol, IPPROTO_UDP);
- EXPECT_EQ(ip.daddr, htonl(INADDR_LOOPBACK));
- EXPECT_EQ(ip.saddr, htonl(INADDR_LOOPBACK));
-
- // Verify the UDP header. We memcpy to deal with pointer aligment.
- struct udphdr udp = {};
- memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
- EXPECT_EQ(udp.dest, kPort);
- EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
-
- // Verify the payload.
- char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
- EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
-}
-
-// Receive via a packet socket.
-TEST_P(CookedPacketTest, Receive) {
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- SendUDPMessage(udp_sock.get());
-
- // Receive and verify the data.
- int loopback_index = GetLoopbackIndex();
- ReceiveMessage(socket_, loopback_index);
-}
-
-// Send via a packet socket.
-TEST_P(CookedPacketTest, Send) {
- // TODO(gvisor.dev/issue/173): Remove once we support packet socket writing.
- SKIP_IF(IsRunningOnGvisor());
-
- // Let's send a UDP packet and receive it using a regular UDP socket.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- struct sockaddr_in bind_addr = {};
- bind_addr.sin_family = AF_INET;
- bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- bind_addr.sin_port = kPort;
- ASSERT_THAT(
- bind(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- SyscallSucceeds());
-
- // Set up the destination physical address.
- struct sockaddr_ll dest = {};
- dest.sll_family = AF_PACKET;
- dest.sll_halen = ETH_ALEN;
- dest.sll_ifindex = GetLoopbackIndex();
- dest.sll_protocol = htons(ETH_P_IP);
- // We're sending to the loopback device, so the address is all 0s.
- memset(dest.sll_addr, 0x00, ETH_ALEN);
-
- // Set up the IP header.
- struct iphdr iphdr = {0};
- iphdr.ihl = 5;
- iphdr.version = 4;
- iphdr.tos = 0;
- iphdr.tot_len =
- htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage));
- // Get a pseudo-random ID. If we clash with an in-use ID the test will fail,
- // but we have no way of getting an ID we know to be good.
- srand(*reinterpret_cast<unsigned int*>(&iphdr));
- iphdr.id = rand();
- // Linux sets this bit ("do not fragment") for small packets.
- iphdr.frag_off = 1 << 6;
- iphdr.ttl = 64;
- iphdr.protocol = IPPROTO_UDP;
- iphdr.daddr = htonl(INADDR_LOOPBACK);
- iphdr.saddr = htonl(INADDR_LOOPBACK);
- iphdr.check = IPChecksum(iphdr);
-
- // Set up the UDP header.
- struct udphdr udphdr = {};
- udphdr.source = kPort;
- udphdr.dest = kPort;
- udphdr.len = htons(sizeof(udphdr) + sizeof(kMessage));
- udphdr.check = UDPChecksum(iphdr, udphdr, kMessage, sizeof(kMessage));
-
- // Copy both headers and the payload into our packet buffer.
- char send_buf[sizeof(iphdr) + sizeof(udphdr) + sizeof(kMessage)];
- memcpy(send_buf, &iphdr, sizeof(iphdr));
- memcpy(send_buf + sizeof(iphdr), &udphdr, sizeof(udphdr));
- memcpy(send_buf + sizeof(iphdr) + sizeof(udphdr), kMessage, sizeof(kMessage));
-
- // Send it.
- ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(send_buf)));
-
- // Wait for the packet to become available on both sockets.
- struct pollfd pfd = {};
- pfd.fd = udp_sock.get();
- pfd.events = POLLIN;
- ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
- pfd.fd = socket_;
- pfd.events = POLLIN;
- ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
-
- // Receive on the packet socket.
- char recv_buf[sizeof(send_buf)];
- ASSERT_THAT(recv(socket_, recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
- ASSERT_EQ(memcmp(recv_buf, send_buf, sizeof(send_buf)), 0);
-
- // Receive on the UDP socket.
- struct sockaddr_in src;
- socklen_t src_len = sizeof(src);
- ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT,
- reinterpret_cast<struct sockaddr*>(&src), &src_len),
- SyscallSucceedsWithValue(sizeof(kMessage)));
- // Check src and payload.
- EXPECT_EQ(strncmp(recv_buf, kMessage, sizeof(kMessage)), 0);
- EXPECT_EQ(src.sin_family, AF_INET);
- EXPECT_EQ(src.sin_port, kPort);
- EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
-}
-
-// Bind and receive via packet socket.
-TEST_P(CookedPacketTest, BindReceive) {
- struct sockaddr_ll bind_addr = {};
- bind_addr.sll_family = AF_PACKET;
- bind_addr.sll_protocol = htons(GetParam());
- bind_addr.sll_ifindex = GetLoopbackIndex();
-
- ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- SyscallSucceeds());
-
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
- SendUDPMessage(udp_sock.get());
-
- // Receive and verify the data.
- ReceiveMessage(socket_, bind_addr.sll_ifindex);
-}
-
-// Double Bind socket.
-TEST_P(CookedPacketTest, DoubleBindSucceeds) {
- struct sockaddr_ll bind_addr = {};
- bind_addr.sll_family = AF_PACKET;
- bind_addr.sll_protocol = htons(GetParam());
- bind_addr.sll_ifindex = GetLoopbackIndex();
-
- ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- SyscallSucceeds());
-
- // Binding socket again should fail.
- ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- // Linux 4.09 returns EINVAL here, but some time before 4.19 it
- // switched to EADDRINUSE.
- SyscallSucceeds());
+TEST_P(PacketSocketCreationTest, Create) {
+ const auto [type, protocol] = GetParam();
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, type, htons(protocol)));
+ EXPECT_GE(fd.get(), 0);
}
-// Bind and verify we do not receive data on interface which is not bound
-TEST_P(CookedPacketTest, BindDrop) {
- // Let's use a simple IP payload: a UDP datagram.
- FileDescriptor udp_sock =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
-
- struct ifaddrs* if_addr_list = nullptr;
- auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
-
- ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
+INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketCreationTest,
+ Combine(Values(SOCK_DGRAM, SOCK_RAW),
+ Values(0, 1, 255, ETH_P_IP, ETH_P_IPV6,
+ std::numeric_limits<uint16_t>::max())));
- // Get interface other than loopback.
- struct ifreq ifr = {};
- for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
- if (strcmp(i->ifa_name, "lo") != 0) {
- strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
- break;
+class PacketSocketTest : public ::testing::TestWithParam<int> {
+ protected:
+ void SetUp() override {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ ASSERT_THAT(socket(AF_PACKET, GetParam(), 0),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP() << "Missing packet socket capability";
}
- }
- // Skip if no interface is available other than loopback.
- if (strlen(ifr.ifr_name) == 0) {
- GTEST_SKIP();
+ socket_ = ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, GetParam(), 0));
}
- // Get interface index.
- EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
-
- // Bind to packet socket requires only family, protocol and ifindex.
- struct sockaddr_ll bind_addr = {};
- bind_addr.sll_family = AF_PACKET;
- bind_addr.sll_protocol = htons(GetParam());
- bind_addr.sll_ifindex = ifr.ifr_ifindex;
-
- ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- sizeof(bind_addr)),
- SyscallSucceeds());
-
- // Send to loopback interface.
- struct sockaddr_in dest = {};
- dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- dest.sin_family = AF_INET;
- dest.sin_port = kPort;
- EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(kMessage)));
+ FileDescriptor socket_;
+};
- // Wait and make sure the socket never receives any data.
- struct pollfd pfd = {};
- pfd.fd = socket_;
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
-}
+TEST_P(PacketSocketTest, RebindProtocol) {
+ const bool kEthHdrIncluded = GetParam() == SOCK_RAW;
-// Verify that we receive outbound packets. This test requires at least one
-// non loopback interface so that we can actually capture an outgoing packet.
-TEST_P(CookedPacketTest, ReceiveOutbound) {
- // Only ETH_P_ALL sockets can receive outbound packets on linux.
- SKIP_IF(GetParam() != ETH_P_ALL);
+ sockaddr_in udp_bind_addr = {
+ .sin_family = AF_INET,
+ .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)},
+ };
- // Let's use a simple IP payload: a UDP datagram.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
-
- struct ifaddrs* if_addr_list = nullptr;
- auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
-
- ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
-
- // Get interface other than loopback.
- struct ifreq ifr = {};
- for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
- if (strcmp(i->ifa_name, "lo") != 0) {
- strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
- break;
- }
- }
-
- // Skip if no interface is available other than loopback.
- if (strlen(ifr.ifr_name) == 0) {
- GTEST_SKIP();
- }
-
- // Get interface index and name.
- EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
- int ifindex = ifr.ifr_ifindex;
-
- constexpr int kMACSize = 6;
- char hwaddr[kMACSize];
- // Get interface address.
- ASSERT_THAT(ioctl(socket_, SIOCGIFHWADDR, &ifr), SyscallSucceeds());
- ASSERT_THAT(ifr.ifr_hwaddr.sa_family,
- AnyOf(Eq(ARPHRD_NONE), Eq(ARPHRD_ETHER)));
- memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, kMACSize);
-
- // Just send it to the google dns server 8.8.8.8. It's UDP we don't care
- // if it actually gets to the DNS Server we just want to see that we receive
- // it on our AF_PACKET socket.
- //
- // NOTE: We just want to pick an IP that is non-local to avoid having to
- // handle ARP as this should cause the UDP packet to be sent to the default
- // gateway configured for the system under test. Otherwise the only packet we
- // will see is the ARP query unless we picked an IP which will actually
- // resolve. The test is a bit brittle but this was the best compromise for
- // now.
- struct sockaddr_in dest = {};
- ASSERT_EQ(inet_pton(AF_INET, "8.8.8.8", &dest.sin_addr.s_addr), 1);
- dest.sin_family = AF_INET;
- dest.sin_port = kPort;
- EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
- reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
- SyscallSucceedsWithValue(sizeof(kMessage)));
-
- // Wait and make sure the socket receives the data.
- struct pollfd pfd = {};
- pfd.fd = socket_;
- pfd.events = POLLIN;
- EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(1));
-
- // Now read and check that the packet is the one we just sent.
- // Read and verify the data.
- constexpr size_t packet_size =
- sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
- char buf[64];
- struct sockaddr_ll src = {};
- socklen_t src_len = sizeof(src);
- ASSERT_THAT(recvfrom(socket_, buf, sizeof(buf), 0,
- reinterpret_cast<struct sockaddr*>(&src), &src_len),
- SyscallSucceedsWithValue(packet_size));
-
- // sockaddr_ll ends with an 8 byte physical address field, but ethernet
- // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
- // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
- // sizeof(sockaddr_ll).
- ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
-
- // Verify the source address.
- EXPECT_EQ(src.sll_family, AF_PACKET);
- EXPECT_EQ(src.sll_ifindex, ifindex);
- EXPECT_EQ(src.sll_halen, ETH_ALEN);
- EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
- EXPECT_EQ(src.sll_pkttype, PACKET_OUTGOING);
- // Verify the link address of the interface matches that of the non
- // non loopback interface address we stored above.
- for (int i = 0; i < src.sll_halen; i++) {
- EXPECT_EQ(src.sll_addr[i], hwaddr[i]);
+ {
+ // Bind the socket so that we have something to send packets to.
+ //
+ // If we didn't do this, the UDP packets we send will be responded to with
+ // ICMP Destination Port Unreachable errors.
+ ASSERT_THAT(
+ bind(udp_sock.get(), reinterpret_cast<const sockaddr*>(&udp_bind_addr),
+ sizeof(udp_bind_addr)),
+ SyscallSucceeds());
+ socklen_t addrlen = sizeof(udp_bind_addr);
+ ASSERT_THAT(
+ getsockname(udp_sock.get(), reinterpret_cast<sockaddr*>(&udp_bind_addr),
+ &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(addrlen, sizeof(udp_bind_addr));
}
- // Verify the IP header.
- struct iphdr ip = {};
- memcpy(&ip, buf, sizeof(ip));
- EXPECT_EQ(ip.ihl, 5);
- EXPECT_EQ(ip.version, 4);
- EXPECT_EQ(ip.tot_len, htons(packet_size));
- EXPECT_EQ(ip.protocol, IPPROTO_UDP);
- EXPECT_EQ(ip.daddr, dest.sin_addr.s_addr);
- EXPECT_NE(ip.saddr, htonl(INADDR_LOOPBACK));
-
- // Verify the UDP header.
- struct udphdr udp = {};
- memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
- EXPECT_EQ(udp.dest, kPort);
- EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
-
- // Verify the payload.
- char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
- EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
-}
+ const int loopback_index = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+
+ auto send_udp_message = [&](const uint64_t v) {
+ ASSERT_THAT(
+ sendto(udp_sock.get(), reinterpret_cast<const char*>(&v), sizeof(v),
+ 0 /* flags */, reinterpret_cast<const sockaddr*>(&udp_bind_addr),
+ sizeof(udp_bind_addr)),
+ SyscallSucceeds());
+ };
+
+ auto bind_to_network_protocol = [&](uint16_t protocol) {
+ const sockaddr_ll packet_bind_addr = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(protocol),
+ .sll_ifindex = loopback_index,
+ };
+
+ ASSERT_THAT(bind(socket_.get(),
+ reinterpret_cast<const sockaddr*>(&packet_bind_addr),
+ sizeof(packet_bind_addr)),
+ SyscallSucceeds());
+ };
+
+ auto test_recv = [&, this](const uint64_t v) {
+ constexpr int kInfiniteTimeout = -1;
+ pollfd pfd = {
+ .fd = socket_.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfiniteTimeout),
+ SyscallSucceedsWithValue(1));
-// Bind with invalid address.
-TEST_P(CookedPacketTest, BindFail) {
- // Null address.
- ASSERT_THAT(
- bind(socket_, nullptr, sizeof(struct sockaddr)),
- AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EINVAL)));
+ struct {
+ ethhdr eth;
+ iphdr ip;
+ udphdr udp;
+ uint64_t payload;
+ char unused;
+ } ABSL_ATTRIBUTE_PACKED read_pkt;
+ sockaddr_ll src;
+ socklen_t src_len = sizeof(src);
+
+ char* buf = reinterpret_cast<char*>(&read_pkt);
+ size_t buflen = sizeof(read_pkt);
+ size_t expected_read_len = sizeof(read_pkt) - sizeof(read_pkt.unused);
+ if (!kEthHdrIncluded) {
+ buf += sizeof(read_pkt.eth);
+ buflen -= sizeof(read_pkt.eth);
+ expected_read_len -= sizeof(read_pkt.eth);
+ }
- // Address of size 1.
- uint8_t addr = 0;
- ASSERT_THAT(
- bind(socket_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
- SyscallFailsWithErrno(EINVAL));
+ ASSERT_THAT(recvfrom(socket_.get(), buf, buflen, 0,
+ reinterpret_cast<sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(expected_read_len));
+ // sockaddr_ll ends with an 8 byte physical address field, but ethernet
+ // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
+ // here, but returns sizeof(sockaddr_ll) since
+ // https://github.com/torvalds/linux/commit/b2cf86e1563e33a14a1c69b3e508d15dc12f804c.
+ ASSERT_THAT(src_len, ::testing::AnyOf(
+ ::testing::Eq(sizeof(src)),
+ ::testing::Eq(sizeof(src) - sizeof(src.sll_addr) +
+ ETH_ALEN)));
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(src.sll_ifindex, loopback_index);
+ EXPECT_EQ(src.sll_halen, ETH_ALEN);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ // This came from the loopback device, so the address is all 0s.
+ constexpr uint8_t allZeroesMAC[ETH_ALEN] = {};
+ EXPECT_EQ(memcmp(src.sll_addr, allZeroesMAC, sizeof(allZeroesMAC)), 0);
+ if (kEthHdrIncluded) {
+ EXPECT_EQ(memcmp(read_pkt.eth.h_dest, allZeroesMAC, sizeof(allZeroesMAC)),
+ 0);
+ EXPECT_EQ(
+ memcmp(read_pkt.eth.h_source, allZeroesMAC, sizeof(allZeroesMAC)), 0);
+ EXPECT_EQ(ntohs(read_pkt.eth.h_proto), ETH_P_IP);
+ }
+ // IHL hold the size of the header in 4 byte units.
+ EXPECT_EQ(read_pkt.ip.ihl, sizeof(iphdr) / 4);
+ EXPECT_EQ(read_pkt.ip.version, IPVERSION);
+ const uint16_t ip_pkt_size =
+ sizeof(read_pkt) - sizeof(read_pkt.eth) - sizeof(read_pkt.unused);
+ EXPECT_EQ(ntohs(read_pkt.ip.tot_len), ip_pkt_size);
+ EXPECT_EQ(read_pkt.ip.protocol, IPPROTO_UDP);
+ EXPECT_EQ(ntohl(read_pkt.ip.daddr), INADDR_LOOPBACK);
+ EXPECT_EQ(ntohl(read_pkt.ip.saddr), INADDR_LOOPBACK);
+ EXPECT_EQ(read_pkt.udp.source, udp_bind_addr.sin_port);
+ EXPECT_EQ(read_pkt.udp.dest, udp_bind_addr.sin_port);
+ EXPECT_EQ(ntohs(read_pkt.udp.len), ip_pkt_size - sizeof(read_pkt.ip));
+ EXPECT_EQ(read_pkt.payload, v);
+ };
+
+ // The packet socket is not bound to IPv4 so we should not receive the sent
+ // message.
+ uint64_t counter = 0;
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+
+ // Bind to IPv4 and expect to receive the UDP packet we send after binding.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(ETH_P_IP));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+ ASSERT_NO_FATAL_FAILURE(test_recv(counter));
+
+ // Bind the packet socket to a random protocol.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(255));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+
+ // Bind back to IPv4 and expect to the UDP packet we send after binding
+ // back to IPv4.
+ ASSERT_NO_FATAL_FAILURE(bind_to_network_protocol(ETH_P_IP));
+ ASSERT_NO_FATAL_FAILURE(send_udp_message(++counter));
+ ASSERT_NO_FATAL_FAILURE(test_recv(counter));
}
-INSTANTIATE_TEST_SUITE_P(AllInetTests, CookedPacketTest,
- ::testing::Values(ETH_P_IP, ETH_P_ALL));
+INSTANTIATE_TEST_SUITE_P(AllPacketSocketTests, PacketSocketTest,
+ Values(SOCK_DGRAM, SOCK_RAW));
} // namespace
diff --git a/test/syscalls/linux/packet_socket_dgram.cc b/test/syscalls/linux/packet_socket_dgram.cc
new file mode 100644
index 000000000..9515a48f2
--- /dev/null
+++ b/test/syscalls/linux/packet_socket_dgram.cc
@@ -0,0 +1,550 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netpacket/packet.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "gtest/gtest.h"
+#include "absl/base/internal/endian.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/capability_util.h"
+#include "test/util/cleanup.h"
+#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
+#include "test/util/test_util.h"
+
+// Some of these tests involve sending packets via AF_PACKET sockets and the
+// loopback interface. Because AF_PACKET circumvents so much of the networking
+// stack, Linux sees these packets as "martian", i.e. they claim to be to/from
+// localhost but don't have the usual associated data. Thus Linux drops them by
+// default. You can see where this happens by following the code at:
+//
+// - net/ipv4/ip_input.c:ip_rcv_finish, which calls
+// - net/ipv4/route.c:ip_route_input_noref, which calls
+// - net/ipv4/route.c:ip_route_input_slow, which finds and drops martian
+// packets.
+//
+// To tell Linux not to drop these packets, you need to tell it to accept our
+// funny packets (which are completely valid and correct, but lack associated
+// in-kernel data because we use AF_PACKET):
+//
+// echo 1 >> /proc/sys/net/ipv4/conf/lo/accept_local
+// echo 1 >> /proc/sys/net/ipv4/conf/lo/route_localnet
+//
+// These tests require CAP_NET_RAW to run.
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+using ::testing::AnyOf;
+using ::testing::Eq;
+
+constexpr char kMessage[] = "soweoneul malhaebwa";
+constexpr in_port_t kPort = 0x409c; // htons(40000)
+
+//
+// "Cooked" tests. Cooked AF_PACKET sockets do not contain link layer
+// headers, and provide link layer destination/source information via a
+// returned struct sockaddr_ll.
+//
+
+// Send kMessage via sock to loopback
+void SendUDPMessage(int sock) {
+ struct sockaddr_in dest = {};
+ dest.sin_port = kPort;
+ dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ dest.sin_family = AF_INET;
+ EXPECT_THAT(sendto(sock, kMessage, sizeof(kMessage), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+}
+
+// Send an IP packet and make sure ETH_P_<something else> doesn't pick it up.
+TEST(BasicCookedPacketTest, WrongType) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, ETH_P_PUP),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP();
+ }
+
+ FileDescriptor sock = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_PUP)));
+
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ SendUDPMessage(udp_sock.get());
+
+ // Wait and make sure the socket never becomes readable.
+ struct pollfd pfd = {};
+ pfd.fd = sock.get();
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
+}
+
+// Tests for "cooked" (SOCK_DGRAM) packet(7) sockets.
+class CookedPacketTest : public ::testing::TestWithParam<int> {
+ protected:
+ // Creates a socket to be used in tests.
+ void SetUp() override;
+
+ // Closes the socket created by SetUp().
+ void TearDown() override;
+
+ // Gets the device index of the loopback device.
+ int GetLoopbackIndex();
+
+ // The socket used for both reading and writing.
+ int socket_;
+};
+
+void CookedPacketTest::SetUp() {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ ASSERT_THAT(socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
+ SyscallFailsWithErrno(EPERM));
+ GTEST_SKIP();
+ }
+
+ if (!IsRunningOnGvisor()) {
+ FileDescriptor acceptLocal = ASSERT_NO_ERRNO_AND_VALUE(
+ Open("/proc/sys/net/ipv4/conf/lo/accept_local", O_RDONLY));
+ FileDescriptor routeLocalnet = ASSERT_NO_ERRNO_AND_VALUE(
+ Open("/proc/sys/net/ipv4/conf/lo/route_localnet", O_RDONLY));
+ char enabled;
+ ASSERT_THAT(read(acceptLocal.get(), &enabled, 1),
+ SyscallSucceedsWithValue(1));
+ ASSERT_EQ(enabled, '1');
+ ASSERT_THAT(read(routeLocalnet.get(), &enabled, 1),
+ SyscallSucceedsWithValue(1));
+ ASSERT_EQ(enabled, '1');
+ }
+
+ ASSERT_THAT(socket_ = socket(AF_PACKET, SOCK_DGRAM, htons(GetParam())),
+ SyscallSucceeds());
+}
+
+void CookedPacketTest::TearDown() {
+ // TearDown will be run even if we skip the test.
+ if (ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
+ EXPECT_THAT(close(socket_), SyscallSucceeds());
+ }
+}
+
+int CookedPacketTest::GetLoopbackIndex() {
+ int v = EXPECT_NO_ERRNO_AND_VALUE(gvisor::testing::GetLoopbackIndex());
+ EXPECT_NE(v, 0);
+ return v;
+}
+
+// Receive and verify the message via packet socket on interface.
+void ReceiveMessage(int sock, int ifindex) {
+ // Wait for the socket to become readable.
+ struct pollfd pfd = {};
+ pfd.fd = sock;
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 2000), SyscallSucceedsWithValue(1));
+
+ // Read and verify the data.
+ constexpr size_t packet_size =
+ sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
+ char buf[64];
+ struct sockaddr_ll src = {};
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(sock, buf, sizeof(buf), 0,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(packet_size));
+
+ // sockaddr_ll ends with an 8 byte physical address field, but ethernet
+ // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
+ // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
+ // sizeof(sockaddr_ll).
+ ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
+
+ // Verify the source address.
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(src.sll_ifindex, ifindex);
+ EXPECT_EQ(src.sll_halen, ETH_ALEN);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ // This came from the loopback device, so the address is all 0s.
+ for (int i = 0; i < src.sll_halen; i++) {
+ EXPECT_EQ(src.sll_addr[i], 0);
+ }
+
+ // Verify the IP header. We memcpy to deal with pointer aligment.
+ struct iphdr ip = {};
+ memcpy(&ip, buf, sizeof(ip));
+ EXPECT_EQ(ip.ihl, 5);
+ EXPECT_EQ(ip.version, 4);
+ EXPECT_EQ(ip.tot_len, htons(packet_size));
+ EXPECT_EQ(ip.protocol, IPPROTO_UDP);
+ EXPECT_EQ(ip.daddr, htonl(INADDR_LOOPBACK));
+ EXPECT_EQ(ip.saddr, htonl(INADDR_LOOPBACK));
+
+ // Verify the UDP header. We memcpy to deal with pointer aligment.
+ struct udphdr udp = {};
+ memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
+ EXPECT_EQ(udp.dest, kPort);
+ EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
+
+ // Verify the payload.
+ char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
+ EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
+}
+
+// Receive via a packet socket.
+TEST_P(CookedPacketTest, Receive) {
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ SendUDPMessage(udp_sock.get());
+
+ // Receive and verify the data.
+ int loopback_index = GetLoopbackIndex();
+ ReceiveMessage(socket_, loopback_index);
+}
+
+// Send via a packet socket.
+TEST_P(CookedPacketTest, Send) {
+ // Let's send a UDP packet and receive it using a regular UDP socket.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ struct sockaddr_in bind_addr = {};
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ bind_addr.sin_port = kPort;
+ ASSERT_THAT(
+ bind(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Set up the destination physical address.
+ struct sockaddr_ll dest = {};
+ dest.sll_family = AF_PACKET;
+ dest.sll_halen = ETH_ALEN;
+ dest.sll_ifindex = GetLoopbackIndex();
+ dest.sll_protocol = htons(ETH_P_IP);
+ // We're sending to the loopback device, so the address is all 0s.
+ memset(dest.sll_addr, 0x00, ETH_ALEN);
+
+ // Set up the IP header.
+ struct iphdr iphdr = {0};
+ iphdr.ihl = 5;
+ iphdr.version = 4;
+ iphdr.tos = 0;
+ iphdr.tot_len =
+ htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage));
+ // Get a pseudo-random ID. If we clash with an in-use ID the test will fail,
+ // but we have no way of getting an ID we know to be good.
+ srand(*reinterpret_cast<unsigned int*>(&iphdr));
+ iphdr.id = rand();
+ // Linux sets this bit ("do not fragment") for small packets.
+ iphdr.frag_off = 1 << 6;
+ iphdr.ttl = 64;
+ iphdr.protocol = IPPROTO_UDP;
+ iphdr.daddr = htonl(INADDR_LOOPBACK);
+ iphdr.saddr = htonl(INADDR_LOOPBACK);
+ iphdr.check = IPChecksum(iphdr);
+
+ // Set up the UDP header.
+ struct udphdr udphdr = {};
+ udphdr.source = kPort;
+ udphdr.dest = kPort;
+ udphdr.len = htons(sizeof(udphdr) + sizeof(kMessage));
+ udphdr.check = UDPChecksum(iphdr, udphdr, kMessage, sizeof(kMessage));
+
+ // Copy both headers and the payload into our packet buffer.
+ char send_buf[sizeof(iphdr) + sizeof(udphdr) + sizeof(kMessage)];
+ memcpy(send_buf, &iphdr, sizeof(iphdr));
+ memcpy(send_buf + sizeof(iphdr), &udphdr, sizeof(udphdr));
+ memcpy(send_buf + sizeof(iphdr) + sizeof(udphdr), kMessage, sizeof(kMessage));
+
+ // Send it.
+ ASSERT_THAT(sendto(socket_, send_buf, sizeof(send_buf), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(send_buf)));
+
+ // Wait for the packet to become available on both sockets.
+ struct pollfd pfd = {};
+ pfd.fd = udp_sock.get();
+ pfd.events = POLLIN;
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
+ pfd.fd = socket_;
+ pfd.events = POLLIN;
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, 5000), SyscallSucceedsWithValue(1));
+
+ // Receive on the packet socket.
+ char recv_buf[sizeof(send_buf)];
+ ASSERT_THAT(recv(socket_, recv_buf, sizeof(recv_buf), 0),
+ SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_EQ(memcmp(recv_buf, send_buf, sizeof(send_buf)), 0);
+
+ // Receive on the UDP socket.
+ struct sockaddr_in src;
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(udp_sock.get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+ // Check src and payload.
+ EXPECT_EQ(strncmp(recv_buf, kMessage, sizeof(kMessage)), 0);
+ EXPECT_EQ(src.sin_family, AF_INET);
+ EXPECT_EQ(src.sin_port, kPort);
+ EXPECT_EQ(src.sin_addr.s_addr, htonl(INADDR_LOOPBACK));
+}
+
+// Bind and receive via packet socket.
+TEST_P(CookedPacketTest, BindReceive) {
+ struct sockaddr_ll bind_addr = {};
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(GetParam());
+ bind_addr.sll_ifindex = GetLoopbackIndex();
+
+ ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ SendUDPMessage(udp_sock.get());
+
+ // Receive and verify the data.
+ ReceiveMessage(socket_, bind_addr.sll_ifindex);
+}
+
+// Double Bind socket.
+TEST_P(CookedPacketTest, DoubleBindSucceeds) {
+ struct sockaddr_ll bind_addr = {};
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(GetParam());
+ bind_addr.sll_ifindex = GetLoopbackIndex();
+
+ ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Binding socket again should fail.
+ ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ // Linux 4.09 returns EINVAL here, but some time before 4.19 it
+ // switched to EADDRINUSE.
+ SyscallSucceeds());
+}
+
+// Bind and verify we do not receive data on interface which is not bound
+TEST_P(CookedPacketTest, BindDrop) {
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+
+ struct ifaddrs* if_addr_list = nullptr;
+ auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
+
+ ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
+
+ // Get interface other than loopback.
+ struct ifreq ifr = {};
+ for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
+ if (strcmp(i->ifa_name, "lo") != 0) {
+ strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
+ break;
+ }
+ }
+
+ // Skip if no interface is available other than loopback.
+ if (strlen(ifr.ifr_name) == 0) {
+ GTEST_SKIP();
+ }
+
+ // Get interface index.
+ EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
+ EXPECT_NE(ifr.ifr_ifindex, 0);
+
+ // Bind to packet socket requires only family, protocol and ifindex.
+ struct sockaddr_ll bind_addr = {};
+ bind_addr.sll_family = AF_PACKET;
+ bind_addr.sll_protocol = htons(GetParam());
+ bind_addr.sll_ifindex = ifr.ifr_ifindex;
+
+ ASSERT_THAT(bind(socket_, reinterpret_cast<struct sockaddr*>(&bind_addr),
+ sizeof(bind_addr)),
+ SyscallSucceeds());
+
+ // Send to loopback interface.
+ struct sockaddr_in dest = {};
+ dest.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ dest.sin_family = AF_INET;
+ dest.sin_port = kPort;
+ EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+
+ // Wait and make sure the socket never receives any data.
+ struct pollfd pfd = {};
+ pfd.fd = socket_;
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(0));
+}
+
+// Verify that we receive outbound packets. This test requires at least one
+// non loopback interface so that we can actually capture an outgoing packet.
+TEST_P(CookedPacketTest, ReceiveOutbound) {
+ // Only ETH_P_ALL sockets can receive outbound packets on linux.
+ SKIP_IF(GetParam() != ETH_P_ALL);
+
+ // Let's use a simple IP payload: a UDP datagram.
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+
+ struct ifaddrs* if_addr_list = nullptr;
+ auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
+
+ ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
+
+ // Get interface other than loopback.
+ struct ifreq ifr = {};
+ for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
+ if (strcmp(i->ifa_name, "lo") != 0) {
+ strncpy(ifr.ifr_name, i->ifa_name, sizeof(ifr.ifr_name));
+ break;
+ }
+ }
+
+ // Skip if no interface is available other than loopback.
+ if (strlen(ifr.ifr_name) == 0) {
+ GTEST_SKIP();
+ }
+
+ // Get interface index and name.
+ EXPECT_THAT(ioctl(socket_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
+ EXPECT_NE(ifr.ifr_ifindex, 0);
+ int ifindex = ifr.ifr_ifindex;
+
+ constexpr int kMACSize = 6;
+ char hwaddr[kMACSize];
+ // Get interface address.
+ ASSERT_THAT(ioctl(socket_, SIOCGIFHWADDR, &ifr), SyscallSucceeds());
+ ASSERT_THAT(ifr.ifr_hwaddr.sa_family,
+ AnyOf(Eq(ARPHRD_NONE), Eq(ARPHRD_ETHER)));
+ memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, kMACSize);
+
+ // Just send it to the google dns server 8.8.8.8. It's UDP we don't care
+ // if it actually gets to the DNS Server we just want to see that we receive
+ // it on our AF_PACKET socket.
+ //
+ // NOTE: We just want to pick an IP that is non-local to avoid having to
+ // handle ARP as this should cause the UDP packet to be sent to the default
+ // gateway configured for the system under test. Otherwise the only packet we
+ // will see is the ARP query unless we picked an IP which will actually
+ // resolve. The test is a bit brittle but this was the best compromise for
+ // now.
+ struct sockaddr_in dest = {};
+ ASSERT_EQ(inet_pton(AF_INET, "8.8.8.8", &dest.sin_addr.s_addr), 1);
+ dest.sin_family = AF_INET;
+ dest.sin_port = kPort;
+ EXPECT_THAT(sendto(udp_sock.get(), kMessage, sizeof(kMessage), 0,
+ reinterpret_cast<struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(kMessage)));
+
+ // Wait and make sure the socket receives the data.
+ struct pollfd pfd = {};
+ pfd.fd = socket_;
+ pfd.events = POLLIN;
+ EXPECT_THAT(RetryEINTR(poll)(&pfd, 1, 1000), SyscallSucceedsWithValue(1));
+
+ // Now read and check that the packet is the one we just sent.
+ // Read and verify the data.
+ constexpr size_t packet_size =
+ sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(kMessage);
+ char buf[64];
+ struct sockaddr_ll src = {};
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(socket_, buf, sizeof(buf), 0,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(packet_size));
+
+ // sockaddr_ll ends with an 8 byte physical address field, but ethernet
+ // addresses only use 6 bytes. Linux used to return sizeof(sockaddr_ll)-2
+ // here, but since commit b2cf86e1563e33a14a1c69b3e508d15dc12f804c returns
+ // sizeof(sockaddr_ll).
+ ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
+
+ // Verify the source address.
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(src.sll_ifindex, ifindex);
+ EXPECT_EQ(src.sll_halen, ETH_ALEN);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ EXPECT_EQ(src.sll_pkttype, PACKET_OUTGOING);
+ // Verify the link address of the interface matches that of the non
+ // non loopback interface address we stored above.
+ for (int i = 0; i < src.sll_halen; i++) {
+ EXPECT_EQ(src.sll_addr[i], hwaddr[i]);
+ }
+
+ // Verify the IP header.
+ struct iphdr ip = {};
+ memcpy(&ip, buf, sizeof(ip));
+ EXPECT_EQ(ip.ihl, 5);
+ EXPECT_EQ(ip.version, 4);
+ EXPECT_EQ(ip.tot_len, htons(packet_size));
+ EXPECT_EQ(ip.protocol, IPPROTO_UDP);
+ EXPECT_EQ(ip.daddr, dest.sin_addr.s_addr);
+ EXPECT_NE(ip.saddr, htonl(INADDR_LOOPBACK));
+
+ // Verify the UDP header.
+ struct udphdr udp = {};
+ memcpy(&udp, buf + sizeof(iphdr), sizeof(udp));
+ EXPECT_EQ(udp.dest, kPort);
+ EXPECT_EQ(udp.len, htons(sizeof(udphdr) + sizeof(kMessage)));
+
+ // Verify the payload.
+ char* payload = reinterpret_cast<char*>(buf + sizeof(iphdr) + sizeof(udphdr));
+ EXPECT_EQ(strncmp(payload, kMessage, sizeof(kMessage)), 0);
+}
+
+// Bind with invalid address.
+TEST_P(CookedPacketTest, BindFail) {
+ // Null address.
+ ASSERT_THAT(
+ bind(socket_, nullptr, sizeof(struct sockaddr)),
+ AnyOf(SyscallFailsWithErrno(EFAULT), SyscallFailsWithErrno(EINVAL)));
+
+ // Address of size 1.
+ uint8_t addr = 0;
+ ASSERT_THAT(
+ bind(socket_, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+INSTANTIATE_TEST_SUITE_P(AllInetTests, CookedPacketTest,
+ ::testing::Values(ETH_P_IP, ETH_P_ALL));
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc
index 72080a272..aa7182b01 100644
--- a/test/syscalls/linux/packet_socket_raw.cc
+++ b/test/syscalls/linux/packet_socket_raw.cc
@@ -13,16 +13,13 @@
// limitations under the License.
#include <arpa/inet.h>
-#include <linux/capability.h>
-#include <linux/filter.h>
-#include <linux/if_arp.h>
-#include <linux/if_packet.h>
#include <net/ethernet.h>
+#include <net/if_arp.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
+#include <netpacket/packet.h>
#include <poll.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
@@ -30,10 +27,12 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/base/internal/endian.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/capability_util.h"
+#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// Some of these tests involve sending packets via AF_PACKET sockets and the
@@ -56,8 +55,6 @@
//
// These tests require CAP_NET_RAW to run.
-// TODO(gvisor.dev/issue/173): gVisor support.
-
namespace gvisor {
namespace testing {
@@ -102,7 +99,7 @@ class RawPacketTest : public ::testing::TestWithParam<int> {
};
void RawPacketTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
ASSERT_THAT(socket(AF_PACKET, SOCK_RAW, htons(GetParam())),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -152,17 +149,15 @@ void RawPacketTest::SetUp() {
void RawPacketTest::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability())) {
EXPECT_THAT(close(s_), SyscallSucceeds());
}
}
int RawPacketTest::GetLoopbackIndex() {
- struct ifreq ifr;
- snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
- EXPECT_THAT(ioctl(s_, SIOCGIFINDEX, &ifr), SyscallSucceeds());
- EXPECT_NE(ifr.ifr_ifindex, 0);
- return ifr.ifr_ifindex;
+ int v = EXPECT_NO_ERRNO_AND_VALUE(gvisor::testing::GetLoopbackIndex());
+ EXPECT_NE(v, 0);
+ return v;
}
// Receive via a packet socket.
@@ -193,7 +188,6 @@ TEST_P(RawPacketTest, Receive) {
// sizeof(sockaddr_ll).
ASSERT_THAT(src_len, AnyOf(Eq(sizeof(src)), Eq(sizeof(src) - 2)));
- // TODO(gvisor.dev/issue/173): Verify protocol once we return it.
// Verify the source address.
EXPECT_EQ(src.sll_family, AF_PACKET);
EXPECT_EQ(src.sll_ifindex, GetLoopbackIndex());
@@ -238,9 +232,6 @@ TEST_P(RawPacketTest, Receive) {
// Send via a packet socket.
TEST_P(RawPacketTest, Send) {
- // TODO(gvisor.dev/issue/173): Remove once we support packet socket writing.
- SKIP_IF(IsRunningOnGvisor());
-
// Let's send a UDP packet and receive it using a regular UDP socket.
FileDescriptor udp_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -338,7 +329,7 @@ TEST_P(RawPacketTest, Send) {
// Check that setting SO_RCVBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover minimum receive buf size by trying to set it to zero.
// See:
@@ -371,7 +362,7 @@ TEST_P(RawPacketTest, SetSocketRecvBufBelowMin) {
// Check that setting SO_RCVBUF above max is clamped to the maximum
// receive buffer size.
TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover max buf size by trying to set the largest possible buffer size.
constexpr int kRcvBufSz = 0xffffffff;
@@ -398,7 +389,7 @@ TEST_P(RawPacketTest, SetSocketRecvBufAboveMax) {
// Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored.
TEST_P(RawPacketTest, SetSocketRecvBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int max = 0;
int min = 0;
@@ -447,7 +438,7 @@ TEST_P(RawPacketTest, SetSocketRecvBuf) {
// Check that setting SO_SNDBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawPacketTest, SetSocketSendBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover minimum buffer size by trying to set it to zero.
constexpr int kSndBufSz = 0;
@@ -478,7 +469,7 @@ TEST_P(RawPacketTest, SetSocketSendBufBelowMin) {
// Check that setting SO_SNDBUF above max is clamped to the maximum
// send buffer size.
TEST_P(RawPacketTest, SetSocketSendBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
// Discover maximum buffer size by trying to set it to a large value.
constexpr int kSndBufSz = 0xffffffff;
@@ -505,7 +496,7 @@ TEST_P(RawPacketTest, SetSocketSendBufAboveMax) {
// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored.
TEST_P(RawPacketTest, SetSocketSendBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int max = 0;
int min = 0;
@@ -549,7 +540,7 @@ TEST_P(RawPacketTest, SetSocketSendBuf) {
}
TEST_P(RawPacketTest, GetSocketError) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int val = 0;
socklen_t val_len = sizeof(val);
@@ -559,7 +550,7 @@ TEST_P(RawPacketTest, GetSocketError) {
}
TEST_P(RawPacketTest, GetSocketErrorBind) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
{
// Bind to the loopback device.
@@ -625,7 +616,7 @@ TEST_P(RawPacketTest, SetSocketDetachFilterNoInstalledFilter) {
}
TEST_P(RawPacketTest, GetSocketDetachFilter) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int val = 0;
socklen_t val_len = sizeof(val);
@@ -634,7 +625,7 @@ TEST_P(RawPacketTest, GetSocketDetachFilter) {
}
TEST_P(RawPacketTest, SetAndGetSocketLinger) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int level = SOL_SOCKET;
int type = SO_LINGER;
@@ -655,7 +646,7 @@ TEST_P(RawPacketTest, SetAndGetSocketLinger) {
}
TEST_P(RawPacketTest, GetSocketAcceptConn) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
int got = -1;
socklen_t length = sizeof(got);
@@ -671,7 +662,7 @@ INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest,
class RawPacketMsgSizeTest : public ::testing::TestWithParam<TestAddress> {};
TEST_P(RawPacketMsgSizeTest, SendTooLong) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
TestAddress addr = GetParam().WithPort(kPort);
@@ -688,8 +679,11 @@ TEST_P(RawPacketMsgSizeTest, SendTooLong) {
SyscallFailsWithErrno(EMSGSIZE));
}
+// TODO(https://fxbug.dev/76957): Run this test on Fuchsia once splice is
+// available.
+#ifndef __Fuchsia__
TEST_P(RawPacketMsgSizeTest, SpliceTooLong) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HavePacketSocketCapability()));
const char buf[65536] = {};
int fds[2];
@@ -716,6 +710,7 @@ TEST_P(RawPacketMsgSizeTest, SpliceTooLong) {
EXPECT_THAT(n, SyscallSucceedsWithValue(sizeof(buf)));
}
}
+#endif // __Fuchsia__
INSTANTIATE_TEST_SUITE_P(AllRawPacketMsgSizeTest, RawPacketMsgSizeTest,
::testing::Values(V4Loopback(), V6Loopback()));
diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc
index 223ddc0c8..1bdfcbbe3 100644
--- a/test/syscalls/linux/partial_bad_buffer.cc
+++ b/test/syscalls/linux/partial_bad_buffer.cc
@@ -26,10 +26,10 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
diff --git a/test/syscalls/linux/ping_socket.cc b/test/syscalls/linux/ping_socket.cc
index 8268e91da..025c9568f 100644
--- a/test/syscalls/linux/ping_socket.cc
+++ b/test/syscalls/linux/ping_socket.cc
@@ -29,8 +29,8 @@
#include "absl/strings/str_join.h"
#include "absl/types/optional.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/socket_util.h"
#include "test/util/test_util.h"
// Note: These tests require /proc/sys/net/ipv4/ping_group_range to be
@@ -128,9 +128,6 @@ std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
{
.bind_to = V4Broadcast(),
.want = EADDRNOTAVAIL,
- // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
- // sockets are no longer allowed to bind to broadcast addresses.
- .want_gvisor = 0,
},
{
.bind_to = V4Loopback(),
@@ -139,9 +136,6 @@ std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
{
.bind_to = V4LoopbackSubnetBroadcast(),
.want = EADDRNOTAVAIL,
- // TODO(gvisor.dev/issue/5711): Remove want_gvisor once ICMP
- // sockets are no longer allowed to bind to broadcast addresses.
- .want_gvisor = 0,
},
{
.bind_to = V4Multicast(),
@@ -155,43 +149,33 @@ std::vector<std::tuple<SocketKind, BindTestCase>> ICMPTestCases() {
.bind_to = V4AddrStr("IPv4UnknownUnicast", "192.168.1.1"),
.want = EADDRNOTAVAIL,
},
- // TODO(gvisor.dev/issue/6021): Remove want_gvisor from all the test
- // cases below once ICMP sockets return EAFNOSUPPORT when binding to
- // IPv6 addresses.
{
.bind_to = V6Any(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6Loopback(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6Multicast(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6MulticastInterfaceLocalAllNodes(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6MulticastLinkLocalAllNodes(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6MulticastLinkLocalAllRouters(),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
{
.bind_to = V6AddrStr("IPv6UnknownUnicast", "fc00::1"),
.want = EAFNOSUPPORT,
- .want_gvisor = EINVAL,
},
});
}
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index 0bba86846..209801e36 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -13,11 +13,13 @@
// limitations under the License.
#include <fcntl.h> /* Obtain O_* constant definitions */
+#include <linux/futex.h>
#include <linux/magic.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/statfs.h>
#include <sys/uio.h>
+#include <syscall.h>
#include <unistd.h>
#include <vector>
@@ -50,6 +52,9 @@ std::atomic<int> global_num_signals_received = 0;
void SigRecordingHandler(int signum, siginfo_t* siginfo,
void* unused_ucontext) {
global_num_signals_received++;
+ ASSERT_THAT(syscall(SYS_futex, &global_num_signals_received,
+ FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT_MAX, 0, 0, 0),
+ SyscallSucceeds());
}
PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) {
@@ -61,11 +66,14 @@ PosixErrorOr<Cleanup> RegisterSignalHandler(int signum) {
return ScopedSigaction(signum, handler);
}
-void WaitForSignalDelivery(absl::Duration timeout, int max_expected) {
- absl::Time wait_start = absl::Now();
- while (global_num_signals_received < max_expected &&
- absl::Now() - wait_start < timeout) {
- absl::SleepFor(absl::Milliseconds(10));
+void WaitForSignalDelivery(int expected) {
+ while (1) {
+ int v = global_num_signals_received;
+ if (v >= expected) {
+ break;
+ }
+ RetryEINTR(syscall)(SYS_futex, &global_num_signals_received,
+ FUTEX_WAIT | FUTEX_PRIVATE_FLAG, v, 0, 0, 0);
}
}
@@ -371,7 +379,7 @@ TEST_P(PipeTest, ReaderSideCloses) {
EXPECT_THAT(write(wfd_.get(), &buf, sizeof(buf)),
SyscallFailsWithErrno(EPIPE));
- WaitForSignalDelivery(absl::Seconds(1), 1);
+ WaitForSignalDelivery(1);
ASSERT_EQ(global_num_signals_received, 1);
}
@@ -411,7 +419,7 @@ TEST_P(PipeTest, BlockWriteClosed) {
notify.WaitForNotification();
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
- WaitForSignalDelivery(absl::Seconds(1), 1);
+ WaitForSignalDelivery(1);
ASSERT_EQ(global_num_signals_received, 1);
t.Join();
@@ -443,7 +451,7 @@ TEST_P(PipeTest, BlockPartialWriteClosed) {
// Unblock the above.
ASSERT_THAT(close(rfd_.release()), SyscallSucceeds());
- WaitForSignalDelivery(absl::Seconds(1), 2);
+ WaitForSignalDelivery(2);
ASSERT_EQ(global_num_signals_received, 2);
t.Join();
diff --git a/test/syscalls/linux/poll.cc b/test/syscalls/linux/poll.cc
index 5ce7e8c8d..ccd084244 100644
--- a/test/syscalls/linux/poll.cc
+++ b/test/syscalls/linux/poll.cc
@@ -116,7 +116,7 @@ void BlockingReadableTest(int16_t mask) {
});
notify.WaitForNotification();
- absl::SleepFor(absl::Seconds(1.0));
+ absl::SleepFor(absl::Seconds(1));
// Write some data to the pipe.
char s[] = "foo\n";
@@ -221,7 +221,7 @@ TEST_F(PollTest, BlockingEventPOLLHUP) {
});
notify.WaitForNotification();
- absl::SleepFor(absl::Seconds(1.0));
+ absl::SleepFor(absl::Seconds(1));
// Write some data and close the writer fd.
fd1.reset();
diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc
index 19a57d353..286b3d168 100644
--- a/test/syscalls/linux/prctl.cc
+++ b/test/syscalls/linux/prctl.cc
@@ -101,11 +101,11 @@ TEST(PrctlTest, NoNewPrivsPreservedAcrossCloneForkAndExecve) {
int no_new_privs;
ASSERT_THAT(no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceeds());
- ScopedThread([] {
+ ScopedThread thread = ScopedThread([] {
ASSERT_THAT(prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0), SyscallSucceeds());
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
- ScopedThread([] {
+ ScopedThread threadInner = ScopedThread([] {
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
// Note that these ASSERT_*s failing will only return from this thread,
@@ -129,9 +129,11 @@ TEST(PrctlTest, NoNewPrivsPreservedAcrossCloneForkAndExecve) {
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
});
+ threadInner.Join();
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(1));
});
+ thread.Join();
EXPECT_THAT(prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0),
SyscallSucceedsWithValue(no_new_privs));
}
@@ -141,7 +143,7 @@ TEST(PrctlTest, PDeathSig) {
// Make the new process' parent a separate thread since the parent death
// signal fires when the parent *thread* exits.
- ScopedThread([&] {
+ ScopedThread thread = ScopedThread([&] {
child_pid = fork();
TEST_CHECK(child_pid >= 0);
if (child_pid == 0) {
@@ -172,6 +174,7 @@ TEST(PrctlTest, PDeathSig) {
// Suppress the SIGSTOP and detach from the child.
ASSERT_THAT(ptrace(PTRACE_DETACH, child_pid, 0, 0), SyscallSucceeds());
});
+ thread.Join();
// The child should have been killed by its parent death SIGKILL.
int status;
@@ -211,6 +214,12 @@ TEST(PrctlTest, RootDumpability) {
SyscallFailsWithErrno(EINVAL));
}
+TEST(PrctlTest, SetGetSubreaper) {
+ // Setting subreaper on PID 1 works vacuously because PID 1 is always a
+ // subreaper.
+ EXPECT_THAT(prctl(PR_SET_CHILD_SUBREAPER, 1), SyscallSucceeds());
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/priority.cc b/test/syscalls/linux/priority.cc
index 1d9bdfa70..e381248e4 100644
--- a/test/syscalls/linux/priority.cc
+++ b/test/syscalls/linux/priority.cc
@@ -72,7 +72,8 @@ TEST(SetpriorityTest, Implemented) {
// No need to clear errno for setpriority():
// "The setpriority() call returns 0 if there is no error, or -1 if there is"
- EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0, /*nice=*/16),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, /*who=*/0,
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
}
@@ -80,7 +81,8 @@ TEST(SetpriorityTest, Implemented) {
TEST(Setpriority, InvalidWhich) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE)));
- EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0, /*nice=*/16),
+ EXPECT_THAT(setpriority(/*which=*/3, /*who=*/0,
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallFailsWithErrno(EINVAL));
}
@@ -88,7 +90,8 @@ TEST(Setpriority, InvalidWhich) {
TEST(SetpriorityTest, ValidWho) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE)));
- EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/16),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(),
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
}
@@ -142,22 +145,26 @@ TEST(SetpriorityTest, OutsideRange) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_NICE)));
// Set niceval > 19
- EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/100),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(),
+ /*nice=*/100), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
errno = 0;
// Test niceval truncated to 19
EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()),
- SyscallSucceedsWithValue(/*maxnice=*/19));
+ SyscallSucceedsWithValue(
+ /*maxnice=*/19)); // NOLINT(bugprone-argument-comment)
// Set niceval < -20
- EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(), /*nice=*/-100),
+ EXPECT_THAT(setpriority(PRIO_PROCESS, getpid(),
+ /*nice=*/-100), // NOLINT(bugprone-argument-comment)
SyscallSucceeds());
errno = 0;
// Test niceval truncated to -20
EXPECT_THAT(getpriority(PRIO_PROCESS, getpid()),
- SyscallSucceedsWithValue(/*minnice=*/-20));
+ SyscallSucceedsWithValue(
+ /*minnice=*/-20)); // NOLINT(bugprone-argument-comment)
}
// Process is not found when which=PRIO_PROCESS
@@ -167,7 +174,7 @@ TEST(SetpriorityTest, InvalidWho) {
// Flaky, but it's tough to avoid a race condition when finding an unused pid
EXPECT_THAT(setpriority(PRIO_PROCESS,
/*who=*/INT_MAX - 1,
- /*nice=*/16),
+ /*nice=*/16), // NOLINT(bugprone-argument-comment)
SyscallFailsWithErrno(ESRCH));
}
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index 78aa73edc..8a4025fed 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -54,6 +54,8 @@
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
@@ -88,6 +90,7 @@ using ::testing::Gt;
using ::testing::HasSubstr;
using ::testing::IsSupersetOf;
using ::testing::Pair;
+using ::testing::StartsWith;
using ::testing::UnorderedElementsAre;
using ::testing::UnorderedElementsAreArray;
@@ -1622,10 +1625,41 @@ TEST(ProcPidStatusTest, HasBasicFields) {
ASSERT_FALSE(status_str.empty());
const auto status = ASSERT_NO_ERRNO_AND_VALUE(ParseProcStatus(status_str));
- EXPECT_THAT(status, IsSupersetOf({Pair("Name", thread_name),
- Pair("Tgid", absl::StrCat(tgid)),
- Pair("Pid", absl::StrCat(tid)),
- Pair("PPid", absl::StrCat(getppid()))}));
+ EXPECT_THAT(status, IsSupersetOf({
+ Pair("Name", thread_name),
+ Pair("Tgid", absl::StrCat(tgid)),
+ Pair("Pid", absl::StrCat(tid)),
+ Pair("PPid", absl::StrCat(getppid())),
+ }));
+
+ if (!IsRunningWithVFS1()) {
+ uid_t ruid, euid, suid;
+ ASSERT_THAT(getresuid(&ruid, &euid, &suid), SyscallSucceeds());
+ gid_t rgid, egid, sgid;
+ ASSERT_THAT(getresgid(&rgid, &egid, &sgid), SyscallSucceeds());
+ std::vector<gid_t> supplementary_gids;
+ int ngids = getgroups(0, nullptr);
+ supplementary_gids.resize(ngids);
+ ASSERT_THAT(getgroups(ngids, supplementary_gids.data()),
+ SyscallSucceeds());
+
+ EXPECT_THAT(
+ status,
+ IsSupersetOf(std::vector<
+ ::testing::Matcher<std::pair<std::string, std::string>>>{
+ // gVisor doesn't support fsuid/gid, and even if it did there is
+ // no getfsuid/getfsgid().
+ Pair("Uid", StartsWith(absl::StrFormat("%d\t%d\t%d\t", ruid, euid,
+ suid))),
+ Pair("Gid", StartsWith(absl::StrFormat("%d\t%d\t%d\t", rgid, egid,
+ sgid))),
+ // ParseProcStatus strips leading whitespace for each value,
+ // so if the Groups line is empty then the trailing space is
+ // stripped.
+ Pair("Groups",
+ StartsWith(absl::StrJoin(supplementary_gids, " "))),
+ }));
+ }
});
}
diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc
index 04fecc02e..672f2dd42 100644
--- a/test/syscalls/linux/proc_net.cc
+++ b/test/syscalls/linux/proc_net.cc
@@ -28,10 +28,10 @@
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -152,6 +152,37 @@ TEST(ProcNetDev, Format) {
EXPECT_GT(entries.size(), 0);
}
+// GetMibsAllocationSysctl retuns a value of the net.core.mibs_allocation
+// sysctl./proc/sys/net/core/mibs_allocation
+//
+// When mibs_allocation is unset, a netns creation inherits MIB from init
+// network namespace. Otherwise, MIBS is allocated for each namespace.
+int GetMibsAllocationSysctl() {
+ auto ret = GetContents("/proc/sys/net/core/mibs_allocation");
+ if (!ret.ok()) {
+ // The current kernel doesn't support mibs_allocation.
+ return 1;
+ }
+ int32_t val;
+ EXPECT_TRUE(absl::SimpleAtoi(ret.ValueOrDie(), &val));
+ return val;
+}
+
+// GetSNMPMetricFromProc retrieves the metric named `item` of `type` from the
+// `snmp` string which represents the file contents of /proc/net/snmp.
+//
+// Note to test writers: If you are writing tests to check the change in value
+// of SNMP metrics, note that if GetMibsAllocationSysctl() == 0
+// (net.core.mibs_allocation isn't set), MIB is from the init network namespace
+// and hence these metrics can have system-wide noise.
+//
+// A feasible way of testing is the following:
+// * Consult RFC 1213 to learn the "SYNTAX" of the metric.
+// * If net.core.mibs_allocation is set:
+// - Can test any metric while checking for hard equality.
+// * If net.core.mibs_allocation is NOT set (system-wide noise is present):
+// - Only test "SYNTAX Counter" metrics.
+// - Counter metrics are increasing in nature, so use LE/GE equality checks.
PosixErrorOr<uint64_t> GetSNMPMetricFromProc(const std::string snmp,
const std::string& type,
const std::string& item) {
@@ -226,12 +257,25 @@ TEST(ProcNetSnmp, TcpReset) {
newAttemptFails = ASSERT_NO_ERRNO_AND_VALUE(
GetSNMPMetricFromProc(snmp, "Tcp", "AttemptFails"));
- EXPECT_EQ(oldActiveOpens, newActiveOpens - 1);
- EXPECT_EQ(oldOutRsts, newOutRsts - 1);
- EXPECT_EQ(oldAttemptFails, newAttemptFails - 1);
+ if (GetMibsAllocationSysctl()) {
+ EXPECT_EQ(oldActiveOpens + 1, newActiveOpens);
+ EXPECT_EQ(oldOutRsts + 1, newOutRsts);
+ EXPECT_EQ(oldAttemptFails + 1, newAttemptFails);
+ } else {
+ // System-wide statistics can have some noise. These metrics should have
+ // increased by at least 1.
+ EXPECT_LE(oldActiveOpens + 1, newActiveOpens);
+ EXPECT_LE(oldOutRsts + 1, newOutRsts);
+ EXPECT_LE(oldAttemptFails + 1, newAttemptFails);
+ }
}
TEST(ProcNetSnmp, TcpEstab) {
+ // This test aims to test the tcpCurrEstab metric which has "SYNTAX Gauge" as
+ // per RFC 1213. Hence, it becomes infeasible to test this when system-wide
+ // statistics have noise.
+ SKIP_IF(GetMibsAllocationSysctl() == 0);
+
// TODO(gvisor.dev/issue/866): epsocket metrics are not savable.
DisableSave ds;
@@ -286,9 +330,9 @@ TEST(ProcNetSnmp, TcpEstab) {
newCurrEstab = ASSERT_NO_ERRNO_AND_VALUE(
GetSNMPMetricFromProc(snmp, "Tcp", "CurrEstab"));
- EXPECT_EQ(oldActiveOpens, newActiveOpens - 1);
- EXPECT_EQ(oldPassiveOpens, newPassiveOpens - 1);
- EXPECT_EQ(oldCurrEstab, newCurrEstab - 2);
+ EXPECT_EQ(oldActiveOpens + 1, newActiveOpens);
+ EXPECT_EQ(oldPassiveOpens + 1, newPassiveOpens);
+ EXPECT_EQ(oldCurrEstab + 2, newCurrEstab);
// Send 1 byte from client to server.
ASSERT_THAT(send(s_connect.get(), "a", 1, 0), SyscallSucceedsWithValue(1));
@@ -355,8 +399,15 @@ TEST(ProcNetSnmp, UdpNoPorts) {
newNoPorts =
ASSERT_NO_ERRNO_AND_VALUE(GetSNMPMetricFromProc(snmp, "Udp", "NoPorts"));
- EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1);
- EXPECT_EQ(oldNoPorts, newNoPorts - 1);
+ if (GetMibsAllocationSysctl()) {
+ EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_EQ(oldNoPorts + 1, newNoPorts);
+ } else {
+ // System-wide statistics can have some noise. These metrics should have
+ // increased by at least 1.
+ EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_LE(oldNoPorts + 1, newNoPorts);
+ }
}
TEST(ProcNetSnmp, UdpIn) {
@@ -405,8 +456,15 @@ TEST(ProcNetSnmp, UdpIn) {
newInDatagrams = ASSERT_NO_ERRNO_AND_VALUE(
GetSNMPMetricFromProc(snmp, "Udp", "InDatagrams"));
- EXPECT_EQ(oldOutDatagrams, newOutDatagrams - 1);
- EXPECT_EQ(oldInDatagrams, newInDatagrams - 1);
+ if (GetMibsAllocationSysctl()) {
+ EXPECT_EQ(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_EQ(oldInDatagrams + 1, newInDatagrams);
+ } else {
+ // System-wide statistics can have some noise. These metrics should have
+ // increased by at least 1.
+ EXPECT_LE(oldOutDatagrams + 1, newOutDatagrams);
+ EXPECT_LE(oldInDatagrams + 1, newInDatagrams);
+ }
}
TEST(ProcNetSnmp, CheckNetStat) {
@@ -498,13 +556,7 @@ TEST(ProcSysNetIpv4Recovery, CanReadAndWrite) {
// Check initial value is set to 1.
EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
SyscallSucceedsWithValue(sizeof(to_write) + 1));
- if (IsRunningOnGvisor()) {
- // TODO(gvisor.dev/issue/5243): TCPRACKLossDetection = 1 should be turned on
- // by default.
- EXPECT_EQ(strcmp(buf, "0\n"), 0);
- } else {
- EXPECT_EQ(strcmp(buf, "1\n"), 0);
- }
+ EXPECT_EQ(strcmp(buf, "1\n"), 0);
// Set tcp_recovery to one of the allowed constants.
EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc
index d519b65e6..f64c23ac0 100644
--- a/test/syscalls/linux/ptrace.cc
+++ b/test/syscalls/linux/ptrace.cc
@@ -30,6 +30,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/flags/flag.h"
+#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/util/capability_util.h"
@@ -51,17 +52,10 @@ ABSL_FLAG(bool, ptrace_test_execve_child, false,
ABSL_FLAG(bool, ptrace_test_trace_descendants_allowed, false,
"If set, run the child workload for "
"PtraceTest_TraceDescendantsAllowed.");
-ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_pid, false,
- "If set, run the child workload for PtraceTest_PrctlSetPtracerPID.");
-ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_any, false,
- "If set, run the child workload for PtraceTest_PrctlSetPtracerAny.");
-ABSL_FLAG(bool, ptrace_test_prctl_clear_ptracer, false,
- "If set, run the child workload for PtraceTest_PrctlClearPtracer.");
-ABSL_FLAG(bool, ptrace_test_prctl_replace_ptracer, false,
- "If set, run the child workload for PtraceTest_PrctlReplacePtracer.");
-ABSL_FLAG(int, ptrace_test_prctl_replace_ptracer_tid, -1,
- "Specifies the replacement tracer tid in the child workload for "
- "PtraceTest_PrctlReplacePtracer.");
+ABSL_FLAG(bool, ptrace_test_ptrace_attacher, false,
+ "If set, run the child workload for PtraceAttacherSubprocess.");
+ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer, false,
+ "If set, run the child workload for PrctlSetPtracerSubprocess.");
ABSL_FLAG(bool, ptrace_test_prctl_set_ptracer_and_exit_tracee_thread, false,
"If set, run the child workload for "
"PtraceTest_PrctlSetPtracerPersistsPastTraceeThreadExit.");
@@ -161,6 +155,86 @@ int CheckPtraceAttach(pid_t pid) {
return 0;
}
+class SimpleSubprocess {
+ public:
+ explicit SimpleSubprocess(absl::string_view child_flag) {
+ int sockets[2];
+ TEST_PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0);
+
+ // Allocate vector before forking (not async-signal-safe).
+ ExecveArray const owned_child_argv = {"/proc/self/exe", child_flag,
+ "--ptrace_test_fd",
+ std::to_string(sockets[0])};
+ char* const* const child_argv = owned_child_argv.get();
+
+ pid_ = fork();
+ if (pid_ == 0) {
+ TEST_PCHECK(close(sockets[1]) == 0);
+ execve(child_argv[0], child_argv, /* envp = */ nullptr);
+ TEST_PCHECK_MSG(false, "Survived execve to test child");
+ }
+ TEST_PCHECK(pid_ > 0);
+ TEST_PCHECK(close(sockets[0]) == 0);
+ sockfd_ = sockets[1];
+ }
+
+ SimpleSubprocess(SimpleSubprocess&& orig)
+ : pid_(orig.pid_), sockfd_(orig.sockfd_) {
+ orig.pid_ = -1;
+ orig.sockfd_ = -1;
+ }
+
+ SimpleSubprocess& operator=(SimpleSubprocess&& orig) {
+ if (this != &orig) {
+ this->~SimpleSubprocess();
+ pid_ = orig.pid_;
+ sockfd_ = orig.sockfd_;
+ orig.pid_ = -1;
+ orig.sockfd_ = -1;
+ }
+ return *this;
+ }
+
+ SimpleSubprocess(SimpleSubprocess const&) = delete;
+ SimpleSubprocess& operator=(SimpleSubprocess const&) = delete;
+
+ ~SimpleSubprocess() {
+ if (pid_ < 0) {
+ return;
+ }
+ EXPECT_THAT(shutdown(sockfd_, SHUT_RDWR), SyscallSucceeds());
+ EXPECT_THAT(close(sockfd_), SyscallSucceeds());
+ int status;
+ EXPECT_THAT(waitpid(pid_, &status, 0), SyscallSucceedsWithValue(pid_));
+ EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
+ << " status " << status;
+ }
+
+ pid_t pid() const { return pid_; }
+
+ // Sends the child process the given value, receives an errno in response, and
+ // returns a PosixError corresponding to the received errno.
+ template <typename T>
+ PosixError Cmd(T val) {
+ if (WriteFd(sockfd_, &val, sizeof(val)) < 0) {
+ return PosixError(errno, "write failed");
+ }
+ return RecvErrno();
+ }
+
+ private:
+ PosixError RecvErrno() {
+ int resp_errno;
+ if (ReadFd(sockfd_, &resp_errno, sizeof(resp_errno)) < 0) {
+ return PosixError(errno, "read failed");
+ }
+ return PosixError(resp_errno);
+ }
+
+ pid_t pid_ = -1;
+ int sockfd_ = -1;
+};
+
TEST(PtraceTest, AttachSelf) {
EXPECT_THAT(ptrace(PTRACE_ATTACH, gettid(), 0, 0),
SyscallFailsWithErrno(EPERM));
@@ -343,289 +417,128 @@ TEST(PtraceTest, PrctlSetPtracerInvalidPID) {
EXPECT_THAT(prctl(PR_SET_PTRACER, 123456789), SyscallFailsWithErrno(EINVAL));
}
-TEST(PtraceTest, PrctlSetPtracerPID) {
- SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
-
- AutoCapability cap(CAP_SYS_PTRACE, false);
-
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
-
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_pid",
- "--ptrace_test_fd", std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
-
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- TEST_PCHECK(close(sockets[1]) == 0);
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
-
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
-
- TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
-
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0);
-
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
+SimpleSubprocess CreatePtraceAttacherSubprocess() {
+ return SimpleSubprocess("--ptrace_test_ptrace_attacher");
}
-[[noreturn]] void RunPrctlSetPtracerPID(int fd) {
- ScopedThread t([fd] {
- // Perform prctl in a separate thread to verify that it is process-wide.
- TEST_PCHECK(prctl(PR_SET_PTRACER, getppid()) == 0);
- MaybeSave();
- // Indicate that the prctl has been set.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
+[[noreturn]] static void RunPtraceAttacher(int sockfd) {
+ // execve() may have restored CAP_SYS_PTRACE if we had real UID 0.
+ TEST_CHECK(SetCapability(CAP_SYS_PTRACE, false).ok());
+ // Perform PTRACE_ATTACH in a separate thread to verify that permissions
+ // apply process-wide.
+ ScopedThread t([&] {
+ while (true) {
+ pid_t pid;
+ int rv = read(sockfd, &pid, sizeof(pid));
+ if (rv == 0) {
+ _exit(0);
+ }
+ if (rv < 0) {
+ _exit(1);
+ }
+ int resp_errno = 0;
+ if (CheckPtraceAttach(pid) < 0) {
+ resp_errno = errno;
+ }
+ TEST_PCHECK(write(sockfd, &resp_errno, sizeof(resp_errno)) ==
+ sizeof(resp_errno));
+ }
});
while (true) {
SleepSafe(absl::Seconds(1));
}
}
-TEST(PtraceTest, PrctlSetPtracerAny) {
- SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
- AutoCapability cap(CAP_SYS_PTRACE, false);
-
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
-
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe", "--ptrace_test_prctl_set_ptracer_any",
- "--ptrace_test_fd", std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
-
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- TEST_PCHECK(close(sockets[1]) == 0);
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
-
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
-
- TEST_PCHECK(CheckPtraceAttach(tracee_pid) == 0);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
-
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
-
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
+SimpleSubprocess CreatePrctlSetPtracerSubprocess() {
+ return SimpleSubprocess("--ptrace_test_prctl_set_ptracer");
}
-[[noreturn]] void RunPrctlSetPtracerAny(int fd) {
- ScopedThread t([fd] {
- // Perform prctl in a separate thread to verify that it is process-wide.
- TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0);
- MaybeSave();
- // Indicate that the prctl has been set.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
+[[noreturn]] static void RunPrctlSetPtracer(int sockfd) {
+ // Perform prctl in a separate thread to verify that it applies
+ // process-wide.
+ ScopedThread t([&] {
+ while (true) {
+ pid_t pid;
+ int rv = read(sockfd, &pid, sizeof(pid));
+ if (rv == 0) {
+ _exit(0);
+ }
+ if (rv < 0) {
+ _exit(1);
+ }
+ int resp_errno = 0;
+ if (prctl(PR_SET_PTRACER, pid) < 0) {
+ resp_errno = errno;
+ }
+ TEST_PCHECK(write(sockfd, &resp_errno, sizeof(resp_errno)) ==
+ sizeof(resp_errno));
+ }
});
while (true) {
SleepSafe(absl::Seconds(1));
}
}
-TEST(PtraceTest, PrctlClearPtracer) {
+TEST(PtraceTest, PrctlSetPtracer) {
SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
- AutoCapability cap(CAP_SYS_PTRACE, false);
-
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
-
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe", "--ptrace_test_prctl_clear_ptracer", "--ptrace_test_fd",
- std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
-
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- TEST_PCHECK(close(sockets[1]) == 0);
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
-
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
-
- TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1);
- TEST_PCHECK(errno == EPERM);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
-
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
-
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
-}
-
-[[noreturn]] void RunPrctlClearPtracer(int fd) {
- ScopedThread t([fd] {
- // Perform prctl in a separate thread to verify that it is process-wide.
- TEST_PCHECK(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) == 0);
- MaybeSave();
- TEST_PCHECK(prctl(PR_SET_PTRACER, 0) == 0);
- MaybeSave();
- // Indicate that the prctl has been set/cleared.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
- });
- while (true) {
- SleepSafe(absl::Seconds(1));
- }
-}
-TEST(PtraceTest, PrctlReplacePtracer) {
- SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1);
AutoCapability cap(CAP_SYS_PTRACE, false);
- pid_t const unused_pid = fork();
- if (unused_pid == 0) {
- while (true) {
- SleepSafe(absl::Seconds(1));
- }
- }
- ASSERT_THAT(unused_pid, SyscallSucceeds());
+ // Ensure that initially, no tracer exception is set.
+ ASSERT_THAT(prctl(PR_SET_PTRACER, 0), SyscallSucceeds());
- // Use sockets to synchronize between tracer and tracee.
- int sockets[2];
- ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds());
+ SimpleSubprocess tracee = CreatePrctlSetPtracerSubprocess();
+ SimpleSubprocess tracer = CreatePtraceAttacherSubprocess();
- // Allocate vector before forking (not async-signal-safe).
- ExecveArray const owned_child_argv = {
- "/proc/self/exe",
- "--ptrace_test_prctl_replace_ptracer",
- "--ptrace_test_prctl_replace_ptracer_tid",
- std::to_string(unused_pid),
- "--ptrace_test_fd",
- std::to_string(sockets[0])};
- char* const* const child_argv = owned_child_argv.get();
+ // By default, Yama should prevent tracer from tracing its parent (this
+ // process) or siblings (tracee).
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(EPERM));
- pid_t const tracee_pid = fork();
- if (tracee_pid == 0) {
- TEST_PCHECK(close(sockets[1]) == 0);
- // This test will create a new thread in the child process.
- // pthread_create(2) isn't async-signal-safe, so we execve() first.
- execve(child_argv[0], child_argv, /* envp = */ nullptr);
- TEST_PCHECK_MSG(false, "Survived execve to test child");
- }
- ASSERT_THAT(tracee_pid, SyscallSucceeds());
- ASSERT_THAT(close(sockets[0]), SyscallSucceeds());
+ // If tracee invokes PR_SET_PTRACER on either tracer's pid, the pid of any of
+ // its ancestors (i.e. us), or PR_SET_PTRACER_ANY, then tracer can trace it
+ // (but not us).
- pid_t const tracer_pid = fork();
- if (tracer_pid == 0) {
- // Wait until tracee has called prctl.
- char done;
- TEST_PCHECK(read(sockets[1], &done, 1) == 1);
- MaybeSave();
+ ASSERT_THAT(tracee.Cmd(tracer.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- TEST_CHECK(CheckPtraceAttach(tracee_pid) == -1);
- TEST_PCHECK(errno == EPERM);
- _exit(0);
- }
- ASSERT_THAT(tracer_pid, SyscallSucceeds());
+ ASSERT_THAT(tracee.Cmd(gettid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- // Clean up tracer.
- int status;
- ASSERT_THAT(waitpid(tracer_pid, &status, 0), SyscallSucceeds());
- EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
- << " status " << status;
+ ASSERT_THAT(tracee.Cmd(static_cast<pid_t>(PR_SET_PTRACER_ANY)),
+ PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- // Clean up tracee.
- ASSERT_THAT(kill(tracee_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(tracee_pid, &status, 0),
- SyscallSucceedsWithValue(tracee_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
+ // If tracee invokes PR_SET_PTRACER with pid 0, then tracer can no longer
+ // trace it.
+ ASSERT_THAT(tracee.Cmd(0), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(EPERM));
- // Clean up unused.
- ASSERT_THAT(kill(unused_pid, SIGKILL), SyscallSucceeds());
- ASSERT_THAT(waitpid(unused_pid, &status, 0),
- SyscallSucceedsWithValue(unused_pid));
- EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL)
- << " status " << status;
-}
+ // If we invoke PR_SET_PTRACER with tracer's pid, then it can trace us (but
+ // not our descendants).
+ ASSERT_THAT(prctl(PR_SET_PTRACER, tracer.pid()), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(tracee.pid()), PosixErrorIs(EPERM));
-[[noreturn]] void RunPrctlReplacePtracer(int new_tracer_pid, int fd) {
- TEST_PCHECK(prctl(PR_SET_PTRACER, getppid()) == 0);
- MaybeSave();
+ // If we invoke PR_SET_PTRACER with pid 0, then tracer can no longer trace us.
+ ASSERT_THAT(prctl(PR_SET_PTRACER, 0), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
- ScopedThread t([new_tracer_pid, fd] {
- TEST_PCHECK(prctl(PR_SET_PTRACER, new_tracer_pid) == 0);
- MaybeSave();
- // Indicate that the prctl has been set.
- TEST_PCHECK(write(fd, "x", 1) == 1);
- MaybeSave();
- });
- while (true) {
- SleepSafe(absl::Seconds(1));
- }
+ // Another thread in our thread group can invoke PR_SET_PTRACER instead; its
+ // effect applies to the whole thread group.
+ pid_t const our_tid = gettid();
+ ScopedThread([&] {
+ ASSERT_THAT(prctl(PR_SET_PTRACER, tracer.pid()), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(0));
+ EXPECT_THAT(tracer.Cmd(our_tid), PosixErrorIs(0));
+
+ ASSERT_THAT(prctl(PR_SET_PTRACER, 0), SyscallSucceeds());
+ EXPECT_THAT(tracer.Cmd(gettid()), PosixErrorIs(EPERM));
+ EXPECT_THAT(tracer.Cmd(our_tid), PosixErrorIs(EPERM));
+ }).Join();
}
// Tests that YAMA exceptions store tracees by thread group leader. Exceptions
@@ -2342,21 +2255,12 @@ int main(int argc, char** argv) {
gvisor::testing::RunTraceDescendantsAllowed(fd);
}
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_pid)) {
- gvisor::testing::RunPrctlSetPtracerPID(fd);
- }
-
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer_any)) {
- gvisor::testing::RunPrctlSetPtracerAny(fd);
- }
-
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_clear_ptracer)) {
- gvisor::testing::RunPrctlClearPtracer(fd);
+ if (absl::GetFlag(FLAGS_ptrace_test_ptrace_attacher)) {
+ gvisor::testing::RunPtraceAttacher(fd);
}
- if (absl::GetFlag(FLAGS_ptrace_test_prctl_replace_ptracer)) {
- gvisor::testing::RunPrctlReplacePtracer(
- absl::GetFlag(FLAGS_ptrace_test_prctl_replace_ptracer_tid), fd);
+ if (absl::GetFlag(FLAGS_ptrace_test_prctl_set_ptracer)) {
+ gvisor::testing::RunPrctlSetPtracer(fd);
}
if (absl::GetFlag(
diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc
index 5ff1f12a0..c6592b4a7 100644
--- a/test/syscalls/linux/pty.cc
+++ b/test/syscalls/linux/pty.cc
@@ -1594,9 +1594,9 @@ TEST_F(JobControlTest, GetForegroundProcessGroupNonControlling) {
// - creates a child process in a new process group
// - sets that child as the foreground process group
// - kills its child and sets itself as the foreground process group.
-// TODO(gvisor.dev/issue/5357): Fix and enable.
-TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroup) {
+TEST_F(JobControlTest, SetForegroundProcessGroup) {
auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
// Ignore SIGTTOU so that we don't stop ourself when calling tcsetpgrp.
@@ -1634,12 +1634,90 @@ TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroup) {
// Set ourself as the foreground process.
pid_t pgid;
- TEST_PCHECK(pgid = getpgid(0) == 0);
+ TEST_PCHECK((pgid = getpgid(0)) >= 0);
TEST_PCHECK(!tcsetpgrp(replica_.get(), pgid));
});
ASSERT_NO_ERRNO(res);
}
+// This test verifies if a SIGTTOU signal is sent to the calling process's group
+// when tcsetpgrp is called by a background process
+TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUBackground) {
+ auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
+ TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
+ pid_t grandchild = fork();
+ if (!grandchild) {
+ // Assign a different pgid to the child so it will result as
+ // a background process.
+ TEST_PCHECK(!setpgid(grandchild, getpid()));
+ TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0)));
+ // We should never reach this.
+ _exit(1);
+ }
+ int wstatus;
+ TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild);
+ TEST_PCHECK(WSTOPSIG(wstatus) == SIGTTOU);
+ });
+ ASSERT_NO_ERRNO(res);
+}
+
+// This test verifies that a SIGTTOU signal is not delivered to
+// a background process which calls tcsetpgrp and is ignoring SIGTTOU
+TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUIgnored) {
+ auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
+ TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
+ pid_t grandchild = fork();
+ if (!grandchild) {
+ // Ignore SIGTTOU so the child in background won't
+ // be stopped when it will call tcsetpgrp
+ struct sigaction sa = {};
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGTTOU, &sa, NULL);
+ // Assign a different pgid to the child so it will result as
+ // a background process.
+ TEST_PCHECK(!setpgid(grandchild, getpid()));
+ TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0)));
+ _exit(0);
+ }
+ int wstatus;
+ TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild);
+ TEST_PCHECK(WSTOPSIG(wstatus) != SIGTTOU);
+ TEST_PCHECK(WIFEXITED(wstatus));
+ });
+ ASSERT_NO_ERRNO(res);
+}
+
+// This test verifies that a SIGTTOU signal is not delivered to
+// a background process which calls tcsetpgrp and is blocking SIGTTOU
+TEST_F(JobControlTest, SetForegroundProcessGroupSIGTTOUBlocked) {
+ auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
+ TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
+ pid_t grandchild = fork();
+ if (!grandchild) {
+ // Block SIGTTOU so the child in background won't
+ // be stopped when it will call tcsetpgrp
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGTTOU);
+ sigprocmask(SIG_BLOCK, &signal_set, NULL);
+ // Assign a different pgid to the child so it will result as
+ // a background process.
+ TEST_PCHECK(!setpgid(grandchild, getpid()));
+ TEST_PCHECK(!tcsetpgrp(replica_.get(), getpgid(0)));
+ _exit(0);
+ }
+ int wstatus;
+ TEST_PCHECK(waitpid(grandchild, &wstatus, WSTOPPED) == grandchild);
+ TEST_PCHECK(WSTOPSIG(wstatus) != SIGTTOU);
+ });
+ ASSERT_NO_ERRNO(res);
+}
+
TEST_F(JobControlTest, SetForegroundProcessGroupWrongTTY) {
pid_t pid = getpid();
ASSERT_THAT(ioctl(replica_.get(), TIOCSPGRP, &pid),
@@ -1657,9 +1735,9 @@ TEST_F(JobControlTest, SetForegroundProcessGroupNegPgid) {
ASSERT_NO_ERRNO(ret);
}
-// TODO(gvisor.dev/issue/5357): Fix and enable.
-TEST_F(JobControlTest, DISABLED_SetForegroundProcessGroupEmptyProcessGroup) {
+TEST_F(JobControlTest, SetForegroundProcessGroupEmptyProcessGroup) {
auto res = RunInChild([=]() {
+ TEST_PCHECK(setsid() >= 0);
TEST_PCHECK(!ioctl(replica_.get(), TIOCSCTTY, 0));
// Create a new process, put it in a new process group, make that group the
diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc
index 69616b400..ef1db47ee 100644
--- a/test/syscalls/linux/raw_socket.cc
+++ b/test/syscalls/linux/raw_socket.cc
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <linux/capability.h>
-#include <linux/filter.h>
+#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
@@ -26,10 +25,10 @@
#include <algorithm>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// Note: in order to run these tests, /proc/sys/net/ipv4/ping_group_range will
@@ -76,6 +75,20 @@ class RawSocketTest : public ::testing::TestWithParam<std::tuple<int, int>> {
return 0;
}
+ uint16_t Port(struct sockaddr* s) {
+ if (Family() == AF_INET) {
+ return ntohs(reinterpret_cast<struct sockaddr_in*>(s)->sin_port);
+ }
+ return ntohs(reinterpret_cast<struct sockaddr_in6*>(s)->sin6_port);
+ }
+
+ void* Addr(struct sockaddr* s) {
+ if (Family() == AF_INET) {
+ return &(reinterpret_cast<struct sockaddr_in*>(s)->sin_addr);
+ }
+ return &(reinterpret_cast<struct sockaddr_in6*>(s)->sin6_addr);
+ }
+
// The socket used for both reading and writing.
int s_;
@@ -84,7 +97,7 @@ class RawSocketTest : public ::testing::TestWithParam<std::tuple<int, int>> {
};
void RawSocketTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
ASSERT_THAT(socket(Family(), SOCK_RAW, Protocol()),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -108,7 +121,7 @@ void RawSocketTest::SetUp() {
void RawSocketTest::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
EXPECT_THAT(close(s_), SyscallSucceeds());
}
}
@@ -117,7 +130,7 @@ void RawSocketTest::TearDown() {
// BasicRawSocket::Setup creates the first one, so we only have to create one
// more here.
TEST_P(RawSocketTest, MultipleCreation) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int s2;
ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds());
@@ -127,7 +140,7 @@ TEST_P(RawSocketTest, MultipleCreation) {
// Test that shutting down an unconnected socket fails.
TEST_P(RawSocketTest, FailShutdownWithoutConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(shutdown(s_, SHUT_WR), SyscallFailsWithErrno(ENOTCONN));
ASSERT_THAT(shutdown(s_, SHUT_RD), SyscallFailsWithErrno(ENOTCONN));
@@ -135,7 +148,7 @@ TEST_P(RawSocketTest, FailShutdownWithoutConnect) {
// Shutdown is a no-op for raw sockets (and datagram sockets in general).
TEST_P(RawSocketTest, ShutdownWriteNoop) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -150,7 +163,7 @@ TEST_P(RawSocketTest, ShutdownWriteNoop) {
// Shutdown is a no-op for raw sockets (and datagram sockets in general).
TEST_P(RawSocketTest, ShutdownReadNoop) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -167,23 +180,71 @@ TEST_P(RawSocketTest, ShutdownReadNoop) {
// Test that listen() fails.
TEST_P(RawSocketTest, FailListen) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(listen(s_, 1), SyscallFailsWithErrno(ENOTSUP));
}
// Test that accept() fails.
TEST_P(RawSocketTest, FailAccept) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr saddr;
socklen_t addrlen;
ASSERT_THAT(accept(s_, &saddr, &addrlen), SyscallFailsWithErrno(ENOTSUP));
}
+TEST_P(RawSocketTest, BindThenGetSockName) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
+
+ struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_);
+ ASSERT_THAT(bind(s_, addr, AddrLen()), SyscallSucceeds());
+ struct sockaddr_storage saddr_storage;
+ struct sockaddr* saddr = reinterpret_cast<struct sockaddr*>(&saddr_storage);
+ socklen_t saddrlen = AddrLen();
+ ASSERT_THAT(getsockname(s_, saddr, &saddrlen), SyscallSucceeds());
+ ASSERT_EQ(saddrlen, AddrLen());
+
+ // The port is expected to hold the protocol number.
+ EXPECT_EQ(Port(saddr), Protocol());
+
+ char addrbuf[INET6_ADDRSTRLEN], saddrbuf[INET6_ADDRSTRLEN];
+ const char* addrstr =
+ inet_ntop(addr->sa_family, Addr(addr), addrbuf, sizeof(addrbuf));
+ ASSERT_NE(addrstr, nullptr);
+ const char* saddrstr =
+ inet_ntop(saddr->sa_family, Addr(saddr), saddrbuf, sizeof(saddrbuf));
+ ASSERT_NE(saddrstr, nullptr);
+ EXPECT_STREQ(saddrstr, addrstr);
+}
+
+TEST_P(RawSocketTest, ConnectThenGetSockName) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
+
+ struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&addr_);
+ ASSERT_THAT(connect(s_, addr, AddrLen()), SyscallSucceeds());
+ struct sockaddr_storage saddr_storage;
+ struct sockaddr* saddr = reinterpret_cast<struct sockaddr*>(&saddr_storage);
+ socklen_t saddrlen = AddrLen();
+ ASSERT_THAT(getsockname(s_, saddr, &saddrlen), SyscallSucceeds());
+ ASSERT_EQ(saddrlen, AddrLen());
+
+ // The port is expected to hold the protocol number.
+ EXPECT_EQ(Port(saddr), Protocol());
+
+ char addrbuf[INET6_ADDRSTRLEN], saddrbuf[INET6_ADDRSTRLEN];
+ const char* addrstr =
+ inet_ntop(addr->sa_family, Addr(addr), addrbuf, sizeof(addrbuf));
+ ASSERT_NE(addrstr, nullptr);
+ const char* saddrstr =
+ inet_ntop(saddr->sa_family, Addr(saddr), saddrbuf, sizeof(saddrbuf));
+ ASSERT_NE(saddrstr, nullptr);
+ EXPECT_STREQ(saddrstr, addrstr);
+}
+
// Test that getpeername() returns nothing before connect().
TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr saddr;
socklen_t addrlen = sizeof(saddr);
@@ -193,7 +254,7 @@ TEST_P(RawSocketTest, FailGetPeerNameBeforeConnect) {
// Test that getpeername() returns something after connect().
TEST_P(RawSocketTest, GetPeerName) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -207,7 +268,7 @@ TEST_P(RawSocketTest, GetPeerName) {
// Test that the socket is writable immediately.
TEST_P(RawSocketTest, PollWritableImmediately) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct pollfd pfd = {};
pfd.fd = s_;
@@ -217,7 +278,7 @@ TEST_P(RawSocketTest, PollWritableImmediately) {
// Test that the socket isn't readable before receiving anything.
TEST_P(RawSocketTest, PollNotReadableInitially) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Try to receive data with MSG_DONTWAIT, which returns immediately if there's
// nothing to be read.
@@ -228,7 +289,7 @@ TEST_P(RawSocketTest, PollNotReadableInitially) {
// Test that the socket becomes readable once something is written to it.
TEST_P(RawSocketTest, PollTriggeredOnWrite) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Write something so that there's data to be read.
// Arbitrary.
@@ -243,7 +304,7 @@ TEST_P(RawSocketTest, PollTriggeredOnWrite) {
// Test that we can connect() to a valid IP (loopback).
TEST_P(RawSocketTest, ConnectToLoopback) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -252,7 +313,7 @@ TEST_P(RawSocketTest, ConnectToLoopback) {
// Test that calling send() without connect() fails.
TEST_P(RawSocketTest, SendWithoutConnectFails) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Arbitrary.
constexpr char kBuf[] = "Endgame was good";
@@ -262,7 +323,7 @@ TEST_P(RawSocketTest, SendWithoutConnectFails) {
// Wildcard Bind.
TEST_P(RawSocketTest, BindToWildcard) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr_storage addr;
addr = {};
@@ -283,16 +344,15 @@ TEST_P(RawSocketTest, BindToWildcard) {
// Bind to localhost.
TEST_P(RawSocketTest, BindToLocalhost) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
}
// Bind to a different address.
TEST_P(RawSocketTest, BindToInvalid) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
struct sockaddr_storage bind_addr = addr_;
if (Family() == AF_INET) {
@@ -304,13 +364,14 @@ TEST_P(RawSocketTest, BindToInvalid) {
memset(&sin6->sin6_addr.s6_addr, 0, sizeof(sin6->sin6_addr.s6_addr));
sin6->sin6_addr.s6_addr[0] = 1; // 1: - An address that we can't bind to.
}
- ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr),
- AddrLen()), SyscallFailsWithErrno(EADDRNOTAVAIL));
+ ASSERT_THAT(
+ bind(s_, reinterpret_cast<struct sockaddr*>(&bind_addr), AddrLen()),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
}
// Send and receive an packet.
TEST_P(RawSocketTest, SendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Arbitrary.
constexpr char kBuf[] = "TB12";
@@ -325,7 +386,7 @@ TEST_P(RawSocketTest, SendAndReceive) {
// We should be able to create multiple raw sockets for the same protocol and
// receive the same packet on both.
TEST_P(RawSocketTest, MultipleSocketReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int s2;
ASSERT_THAT(s2 = socket(Family(), SOCK_RAW, Protocol()), SyscallSucceeds());
@@ -340,11 +401,11 @@ TEST_P(RawSocketTest, MultipleSocketReceive) {
// Receive it on socket 2.
std::vector<char> recv_buf2(sizeof(kBuf) + HdrLen());
- ASSERT_NO_FATAL_FAILURE(ReceiveBufFrom(s2, recv_buf2.data(),
- recv_buf2.size()));
+ ASSERT_NO_FATAL_FAILURE(
+ ReceiveBufFrom(s2, recv_buf2.data(), recv_buf2.size()));
- EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(),
- recv_buf2.data() + HdrLen(), sizeof(kBuf)),
+ EXPECT_EQ(memcmp(recv_buf1.data() + HdrLen(), recv_buf2.data() + HdrLen(),
+ sizeof(kBuf)),
0);
ASSERT_THAT(close(s2), SyscallSucceeds());
@@ -352,7 +413,7 @@ TEST_P(RawSocketTest, MultipleSocketReceive) {
// Test that connect sends packets to the right place.
TEST_P(RawSocketTest, SendAndReceiveViaConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
@@ -371,11 +432,10 @@ TEST_P(RawSocketTest, SendAndReceiveViaConnect) {
// Bind to localhost, then send and receive packets.
TEST_P(RawSocketTest, BindSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
// Arbitrary.
constexpr char kBuf[] = "DR16";
@@ -389,11 +449,10 @@ TEST_P(RawSocketTest, BindSendAndReceive) {
// Bind and connect to localhost and send/receive packets.
TEST_P(RawSocketTest, BindConnectSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
SyscallSucceeds());
@@ -411,7 +470,7 @@ TEST_P(RawSocketTest, BindConnectSendAndReceive) {
// Check that setting SO_RCVBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover minimum receive buf size by trying to set it to zero.
// See:
@@ -444,7 +503,7 @@ TEST_P(RawSocketTest, SetSocketRecvBufBelowMin) {
// Check that setting SO_RCVBUF above max is clamped to the maximum
// receive buffer size.
TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover max buf size by trying to set the largest possible buffer size.
constexpr int kRcvBufSz = 0xffffffff;
@@ -471,7 +530,7 @@ TEST_P(RawSocketTest, SetSocketRecvBufAboveMax) {
// Check that setting SO_RCVBUF min <= kRcvBufSz <= max is honored.
TEST_P(RawSocketTest, SetSocketRecvBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int max = 0;
int min = 0;
@@ -521,7 +580,7 @@ TEST_P(RawSocketTest, SetSocketRecvBuf) {
// Check that setting SO_SNDBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(RawSocketTest, SetSocketSendBufBelowMin) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover minimum buffer size by trying to set it to zero.
constexpr int kSndBufSz = 0;
@@ -552,7 +611,7 @@ TEST_P(RawSocketTest, SetSocketSendBufBelowMin) {
// Check that setting SO_SNDBUF above max is clamped to the maximum
// send buffer size.
TEST_P(RawSocketTest, SetSocketSendBufAboveMax) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Discover maximum buffer size by trying to set it to a large value.
constexpr int kSndBufSz = 0xffffffff;
@@ -579,7 +638,7 @@ TEST_P(RawSocketTest, SetSocketSendBufAboveMax) {
// Check that setting SO_SNDBUF min <= kSndBufSz <= max is honored.
TEST_P(RawSocketTest, SetSocketSendBuf) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int max = 0;
int min = 0;
@@ -625,11 +684,10 @@ TEST_P(RawSocketTest, SetSocketSendBuf) {
// Test that receive buffer limits are not enforced when the recv buffer is
// empty.
TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
SyscallSucceeds());
@@ -656,9 +714,7 @@ TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) {
// Receive the packet and make sure it's identical.
std::vector<char> recv_buf(buf.size() + HdrLen());
ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size()));
- EXPECT_EQ(
- memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()),
- 0);
+ EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0);
}
{
@@ -671,9 +727,7 @@ TEST_P(RawSocketTest, RecvBufLimitsEmptyRecvBuffer) {
// Receive the packet and make sure it's identical.
std::vector<char> recv_buf(buf.size() + HdrLen());
ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size()));
- EXPECT_EQ(
- memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()),
- 0);
+ EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0);
}
}
@@ -687,11 +741,10 @@ TEST_P(RawSocketTest, RecvBufLimits) {
if (Protocol() == IPPROTO_TCP) {
return;
}
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
- ASSERT_THAT(
- bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
- SyscallSucceeds());
+ ASSERT_THAT(bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
+ SyscallSucceeds());
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), AddrLen()),
SyscallSucceeds());
@@ -751,9 +804,7 @@ TEST_P(RawSocketTest, RecvBufLimits) {
// Receive the packet and make sure it's identical.
std::vector<char> recv_buf(buf.size() + HdrLen());
ASSERT_NO_FATAL_FAILURE(ReceiveBuf(recv_buf.data(), recv_buf.size()));
- EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(),
- buf.size()),
- 0);
+ EXPECT_EQ(memcmp(recv_buf.data() + HdrLen(), buf.data(), buf.size()), 0);
}
// Assert that the last packet is dropped because the receive buffer should
@@ -822,7 +873,7 @@ TEST_P(RawSocketTest, GetSocketDetachFilter) {
// AF_INET6+SOCK_RAW+IPPROTO_RAW sockets can be created, but not written to.
TEST(RawSocketTest, IPv6ProtoRaw) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int sock;
ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW),
@@ -839,7 +890,7 @@ TEST(RawSocketTest, IPv6ProtoRaw) {
}
TEST(RawSocketTest, IPv6SendMsg) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int sock;
ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP),
@@ -867,7 +918,7 @@ TEST(RawSocketTest, IPv6SendMsg) {
}
TEST_P(RawSocketTest, ConnectOnIPv6Socket) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int sock;
ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP),
@@ -888,6 +939,124 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Combine(::testing::Values(IPPROTO_TCP, IPPROTO_UDP),
::testing::Values(AF_INET, AF_INET6)));
+void TestRawSocketMaybeBindReceive(bool do_bind) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
+
+ constexpr char payload[] = "abcdefgh";
+
+ const sockaddr_in addr = {
+ .sin_family = AF_INET,
+ .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)},
+ };
+
+ FileDescriptor udp_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
+ sockaddr_in udp_sock_bind_addr = addr;
+ socklen_t udp_sock_bind_addr_len = sizeof(udp_sock_bind_addr);
+ ASSERT_THAT(bind(udp_sock.get(),
+ reinterpret_cast<const sockaddr*>(&udp_sock_bind_addr),
+ sizeof(udp_sock_bind_addr)),
+ SyscallSucceeds());
+ ASSERT_THAT(getsockname(udp_sock.get(),
+ reinterpret_cast<sockaddr*>(&udp_sock_bind_addr),
+ &udp_sock_bind_addr_len),
+ SyscallSucceeds());
+ ASSERT_EQ(udp_sock_bind_addr_len, sizeof(udp_sock_bind_addr));
+
+ FileDescriptor raw_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_UDP));
+
+ auto test_recv = [&](const char* scope, uint32_t expected_destination) {
+ SCOPED_TRACE(scope);
+
+ constexpr int kInfinitePollTimeout = -1;
+ pollfd pfd = {
+ .fd = raw_sock.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfinitePollTimeout),
+ SyscallSucceedsWithValue(1));
+
+ struct ipv4_udp_packet {
+ iphdr ip;
+ udphdr udp;
+ char data[sizeof(payload)];
+
+ // Used to make sure only the required space is used.
+ char unused_space;
+ } ABSL_ATTRIBUTE_PACKED;
+ constexpr size_t kExpectedIPPacketSize =
+ offsetof(ipv4_udp_packet, unused_space);
+
+ // Receive the whole IPv4 packet on the raw socket.
+ ipv4_udp_packet read_raw_packet;
+ sockaddr_in peer;
+ socklen_t peerlen = sizeof(peer);
+ ASSERT_EQ(
+ recvfrom(raw_sock.get(), reinterpret_cast<char*>(&read_raw_packet),
+ sizeof(read_raw_packet), 0 /* flags */,
+ reinterpret_cast<sockaddr*>(&peer), &peerlen),
+ static_cast<ssize_t>(kExpectedIPPacketSize))
+ << strerror(errno);
+ ASSERT_EQ(peerlen, sizeof(peer));
+ EXPECT_EQ(read_raw_packet.ip.version, static_cast<unsigned int>(IPVERSION));
+ // IHL holds the number of header bytes in 4 byte units.
+ EXPECT_EQ(read_raw_packet.ip.ihl, sizeof(read_raw_packet.ip) / 4);
+ EXPECT_EQ(ntohs(read_raw_packet.ip.tot_len), kExpectedIPPacketSize);
+ EXPECT_EQ(ntohs(read_raw_packet.ip.frag_off) & IP_OFFMASK, 0);
+ EXPECT_EQ(read_raw_packet.ip.protocol, SOL_UDP);
+ EXPECT_EQ(ntohl(read_raw_packet.ip.saddr), INADDR_LOOPBACK);
+ EXPECT_EQ(ntohl(read_raw_packet.ip.daddr), expected_destination);
+ EXPECT_EQ(read_raw_packet.udp.source, udp_sock_bind_addr.sin_port);
+ EXPECT_EQ(read_raw_packet.udp.dest, udp_sock_bind_addr.sin_port);
+ EXPECT_EQ(ntohs(read_raw_packet.udp.len),
+ kExpectedIPPacketSize - sizeof(read_raw_packet.ip));
+ for (size_t i = 0; i < sizeof(payload); i++) {
+ EXPECT_EQ(read_raw_packet.data[i], payload[i])
+ << "byte mismatch @ idx=" << i;
+ }
+ EXPECT_EQ(peer.sin_family, AF_INET);
+ EXPECT_EQ(peer.sin_port, 0);
+ EXPECT_EQ(ntohl(peer.sin_addr.s_addr), INADDR_LOOPBACK);
+ };
+
+ if (do_bind) {
+ ASSERT_THAT(bind(raw_sock.get(), reinterpret_cast<const sockaddr*>(&addr),
+ sizeof(addr)),
+ SyscallSucceeds());
+ }
+
+ constexpr int kSendToFlags = 0;
+ sockaddr_in different_addr = udp_sock_bind_addr;
+ different_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK + 1);
+ ASSERT_THAT(sendto(udp_sock.get(), payload, sizeof(payload), kSendToFlags,
+ reinterpret_cast<const sockaddr*>(&different_addr),
+ sizeof(different_addr)),
+ SyscallSucceedsWithValue(sizeof(payload)));
+ if (!do_bind) {
+ ASSERT_NO_FATAL_FAILURE(
+ test_recv("different_addr", ntohl(different_addr.sin_addr.s_addr)));
+ }
+ ASSERT_THAT(sendto(udp_sock.get(), payload, sizeof(payload), kSendToFlags,
+ reinterpret_cast<const sockaddr*>(&udp_sock_bind_addr),
+ sizeof(udp_sock_bind_addr)),
+ SyscallSucceedsWithValue(sizeof(payload)));
+ ASSERT_NO_FATAL_FAILURE(
+ test_recv("addr", ntohl(udp_sock_bind_addr.sin_addr.s_addr)));
+}
+
+TEST(RawSocketTest, UnboundReceive) {
+ // Test that a raw socket receives packets destined to any address if it is
+ // not bound to an address.
+ ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(false /* do_bind */));
+}
+
+TEST(RawSocketTest, BindReceive) {
+ // Test that a raw socket only receives packets destined to the address it is
+ // bound to.
+ ASSERT_NO_FATAL_FAILURE(TestRawSocketMaybeBindReceive(true /* do_bind */));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc
index 8b3d02d97..d45bd07bc 100644
--- a/test/syscalls/linux/raw_socket_hdrincl.cc
+++ b/test/syscalls/linux/raw_socket_hdrincl.cc
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <linux/capability.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
@@ -27,10 +26,10 @@
#include "gtest/gtest.h"
#include "absl/base/internal/endian.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -63,7 +62,7 @@ class RawHDRINCL : public ::testing::Test {
};
void RawHDRINCL::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_RAW),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -81,7 +80,7 @@ void RawHDRINCL::SetUp() {
void RawHDRINCL::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
EXPECT_THAT(close(socket_), SyscallSucceeds());
}
}
@@ -177,11 +176,10 @@ TEST_F(RawHDRINCL, ConnectToLoopback) {
SyscallSucceeds());
}
-// FIXME(gvisor.dev/issue/3159): Test currently flaky.
-TEST_F(RawHDRINCL, DISABLED_SendWithoutConnectSucceeds) {
+TEST_F(RawHDRINCL, SendWithoutConnectFails) {
struct iphdr hdr = LoopbackHeader();
ASSERT_THAT(send(socket_, &hdr, sizeof(hdr), 0),
- SyscallSucceedsWithValue(sizeof(hdr)));
+ SyscallFailsWithErrno(EDESTADDRREQ));
}
// HDRINCL implies write-only. Verify that we can't read a packet sent to
@@ -282,9 +280,6 @@ TEST_F(RawHDRINCL, SendAndReceive) {
// Send and receive a packet where the sendto address is not the same as the
// provided destination.
TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
- // FIXME(gvisor.dev/issue/3160): Test currently flaky.
- SKIP_IF(true);
-
int port = 40000;
if (!IsRunningOnGvisor()) {
port = static_cast<short>(ASSERT_NO_ERRNO_AND_VALUE(
@@ -302,18 +297,20 @@ TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
ASSERT_TRUE(
FillPacket(packet, sizeof(packet), port, kPayload, sizeof(kPayload)));
// Overwrite the IP destination address with an IP we can't get to.
+ constexpr int32_t kUnreachable = 42;
struct iphdr iphdr = {};
memcpy(&iphdr, packet, sizeof(iphdr));
- iphdr.daddr = 42;
+ iphdr.daddr = kUnreachable;
memcpy(packet, &iphdr, sizeof(iphdr));
+ // Send to localhost via loopback.
socklen_t addrlen = sizeof(addr_);
ASSERT_NO_FATAL_FAILURE(sendto(socket_, &packet, sizeof(packet), 0,
reinterpret_cast<struct sockaddr*>(&addr_),
addrlen));
- // Receive the payload, since sendto should replace the bad destination with
- // localhost.
+ // Receive the payload. Despite an unreachable destination address, sendto
+ // should have sent the packet through loopback.
char recv_buf[sizeof(packet)];
struct sockaddr_in src;
socklen_t src_size = sizeof(src);
@@ -331,9 +328,8 @@ TEST_F(RawHDRINCL, SendAndReceiveDifferentAddress) {
struct iphdr recv_iphdr = {};
memcpy(&recv_iphdr, recv_buf, sizeof(recv_iphdr));
EXPECT_NE(recv_iphdr.id, 0);
- // The destination address should be localhost, not the bad IP we set
- // initially.
- EXPECT_EQ(absl::gbswap_32(recv_iphdr.daddr), INADDR_LOOPBACK);
+ // The destination address is kUnreachable despite arriving via loopback.
+ EXPECT_EQ(recv_iphdr.daddr, kUnreachable);
}
// Send and receive a packet w/ the IP_HDRINCL option set.
diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc
index bd779da92..3f9717284 100644
--- a/test/syscalls/linux/raw_socket_icmp.cc
+++ b/test/syscalls/linux/raw_socket_icmp.cc
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <linux/capability.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
@@ -24,10 +23,10 @@
#include <cstdint>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -77,7 +76,7 @@ class RawSocketICMPTest : public ::testing::Test {
};
void RawSocketICMPTest::SetUp() {
- if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
ASSERT_THAT(socket(AF_INET, SOCK_RAW, IPPROTO_ICMP),
SyscallFailsWithErrno(EPERM));
GTEST_SKIP();
@@ -95,7 +94,7 @@ void RawSocketICMPTest::SetUp() {
void RawSocketICMPTest::TearDown() {
// TearDown will be run even if we skip the test.
- if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))) {
+ if (ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability())) {
EXPECT_THAT(close(s_), SyscallSucceeds());
}
}
@@ -103,7 +102,7 @@ void RawSocketICMPTest::TearDown() {
// We'll only read an echo in this case, as the kernel won't respond to the
// malformed ICMP checksum.
TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Prepare and send an ICMP packet. Use arbitrary junk for checksum, sequence,
// and ID. None of that should matter for raw sockets - the kernel should
@@ -132,7 +131,7 @@ TEST_F(RawSocketICMPTest, SendAndReceiveBadChecksum) {
// Send and receive an ICMP packet.
TEST_F(RawSocketICMPTest, SendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
// Prepare and send an ICMP packet. Use arbitrary junk for sequence and ID.
// None of that should matter for raw sockets - the kernel should still give
@@ -152,7 +151,7 @@ TEST_F(RawSocketICMPTest, SendAndReceive) {
// We should be able to create multiple raw sockets for the same protocol and
// receive the same packet on both.
TEST_F(RawSocketICMPTest, MultipleSocketReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor s2 =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_RAW, IPPROTO_ICMP));
@@ -215,7 +214,7 @@ TEST_F(RawSocketICMPTest, MultipleSocketReceive) {
// A raw ICMP socket and ping socket should both receive the ICMP packets
// intended for the ping socket.
TEST_F(RawSocketICMPTest, RawAndPingSockets) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor ping_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
@@ -262,10 +261,10 @@ TEST_F(RawSocketICMPTest, RawAndPingSockets) {
}
// A raw ICMP socket should be able to send a malformed short ICMP Echo Request,
-// while ping socket should not.
-// Neither should be able to receieve a short malformed packet.
+// while a ping socket should not. Neither should be able to receieve a short
+// malformed packet.
TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor ping_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
@@ -306,7 +305,7 @@ TEST_F(RawSocketICMPTest, ShortEchoRawAndPingSockets) {
// while ping socket should not.
// Neither should be able to receieve a short malformed packet.
TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
FileDescriptor ping_sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP));
@@ -345,7 +344,7 @@ TEST_F(RawSocketICMPTest, ShortEchoReplyRawAndPingSockets) {
// Test that connect() sends packets to the right place.
TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
connect(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
@@ -369,7 +368,7 @@ TEST_F(RawSocketICMPTest, SendAndReceiveViaConnect) {
// Bind to localhost, then send and receive packets.
TEST_F(RawSocketICMPTest, BindSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
@@ -392,7 +391,7 @@ TEST_F(RawSocketICMPTest, BindSendAndReceive) {
// Bind and connect to localhost and send/receive packets.
TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
ASSERT_THAT(
bind(s_, reinterpret_cast<struct sockaddr*>(&addr_), sizeof(addr_)),
@@ -418,7 +417,7 @@ TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) {
// Set and get SO_LINGER.
TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int level = SOL_SOCKET;
int type = SO_LINGER;
@@ -440,7 +439,7 @@ TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
// Test getsockopt for SO_ACCEPTCONN.
TEST_F(RawSocketICMPTest, GetSocketAcceptConn) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
int got = -1;
socklen_t length = sizeof(got);
diff --git a/test/syscalls/linux/readahead.cc b/test/syscalls/linux/readahead.cc
index 71073bb3c..04104c912 100644
--- a/test/syscalls/linux/readahead.cc
+++ b/test/syscalls/linux/readahead.cc
@@ -16,8 +16,8 @@
#include <fcntl.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc
index f72957f89..87b66aa98 100644
--- a/test/syscalls/linux/semaphore.cc
+++ b/test/syscalls/linux/semaphore.cc
@@ -1019,6 +1019,17 @@ TEST(SemaphoreTest, SemInfo) {
EXPECT_EQ(info.semvmx, kSemVmx);
}
+TEST(SempahoreTest, RemoveNonExistentSemaphore) {
+ EXPECT_THAT(semctl(-1, 0, IPC_RMID), SyscallFailsWithErrno(EINVAL));
+}
+
+TEST(SempahoreTest, RemoveDeletedSemaphore) {
+ int id;
+ EXPECT_THAT(id = semget(IPC_PRIVATE, 1, 0), SyscallSucceeds());
+ EXPECT_THAT(semctl(id, 0, IPC_RMID), SyscallSucceeds());
+ EXPECT_THAT(semctl(id, 0, IPC_RMID), SyscallFailsWithErrno(EINVAL));
+}
+
} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc
index bea4ee71c..9bd3bd5e8 100644
--- a/test/syscalls/linux/sendfile.cc
+++ b/test/syscalls/linux/sendfile.cc
@@ -208,38 +208,6 @@ TEST(SendFileTest, SendAndUpdateFileOffset) {
absl::string_view(actual, kHalfDataSize));
}
-TEST(SendFileTest, SendToDevZeroAndUpdateFileOffset) {
- // Create temp files.
- // Test input string length must be > 2 AND even.
- constexpr char kData[] = "The slings and arrows of outrageous fortune,";
- constexpr int kDataSize = sizeof(kData) - 1;
- constexpr int kHalfDataSize = kDataSize / 2;
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- GetAbsoluteTestTmpdir(), kData, TempPath::kDefaultFileMode));
-
- // Open the input file as read only.
- const FileDescriptor inf =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDONLY));
-
- // Open /dev/zero as write only.
- const FileDescriptor outf =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY));
-
- // Send data and verify that sendfile returns the correct value.
- int bytes_sent;
- EXPECT_THAT(
- bytes_sent = sendfile(outf.get(), inf.get(), nullptr, kHalfDataSize),
- SyscallSucceedsWithValue(kHalfDataSize));
-
- char actual[kHalfDataSize];
- // Verify that the input file offset has been updated.
- ASSERT_THAT(read(inf.get(), &actual, kDataSize - bytes_sent),
- SyscallSucceedsWithValue(kHalfDataSize));
- EXPECT_EQ(
- absl::string_view(kData + kDataSize - bytes_sent, kDataSize - bytes_sent),
- absl::string_view(actual, kHalfDataSize));
-}
-
TEST(SendFileTest, SendAndUpdateFileOffsetFromNonzeroStartingPoint) {
// Create temp files.
// Test input string length must be > 2 AND divisible by 4.
@@ -609,23 +577,6 @@ TEST(SendFileTest, SendPipeBlocks) {
SyscallSucceedsWithValue(kDataSize));
}
-TEST(SendFileTest, SendToSpecialFile) {
- // Create temp file.
- const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
- GetAbsoluteTestTmpdir(), "", TempPath::kDefaultFileMode));
-
- const FileDescriptor inf =
- ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
- constexpr int kSize = 0x7ff;
- ASSERT_THAT(ftruncate(inf.get(), kSize), SyscallSucceeds());
-
- auto eventfd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
-
- // eventfd can accept a number of bytes which is a multiple of 8.
- EXPECT_THAT(sendfile(eventfd.get(), inf.get(), nullptr, 0xfffff),
- SyscallSucceedsWithValue(kSize & (~7)));
-}
-
TEST(SendFileTest, SendFileToPipe) {
// Create temp file.
constexpr char kData[] = "<insert-quote-here>";
@@ -672,57 +623,6 @@ TEST(SendFileTest, SendFileToSelf) {
SyscallSucceedsWithValue(kSendfileSize));
}
-static volatile int signaled = 0;
-void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; }
-
-TEST(SendFileTest, ToEventFDDoesNotSpin) {
- FileDescriptor efd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0));
-
- // Write the maximum value of an eventfd to a file.
- const uint64_t kMaxEventfdValue = 0xfffffffffffffffe;
- const auto tempfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- const auto tempfd = ASSERT_NO_ERRNO_AND_VALUE(Open(tempfile.path(), O_RDWR));
- ASSERT_THAT(
- pwrite(tempfd.get(), &kMaxEventfdValue, sizeof(kMaxEventfdValue), 0),
- SyscallSucceedsWithValue(sizeof(kMaxEventfdValue)));
-
- // Set the eventfd's value to 1.
- const uint64_t kOne = 1;
- ASSERT_THAT(write(efd.get(), &kOne, sizeof(kOne)),
- SyscallSucceedsWithValue(sizeof(kOne)));
-
- // Set up signal handler.
- struct sigaction sa = {};
- sa.sa_sigaction = SigUsr1Handler;
- sa.sa_flags = SA_SIGINFO;
- const auto cleanup_sigact =
- ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGUSR1, sa));
-
- // Send SIGUSR1 to this thread in 1 second.
- struct sigevent sev = {};
- sev.sigev_notify = SIGEV_THREAD_ID;
- sev.sigev_signo = SIGUSR1;
- sev.sigev_notify_thread_id = gettid();
- auto timer = ASSERT_NO_ERRNO_AND_VALUE(TimerCreate(CLOCK_MONOTONIC, sev));
- struct itimerspec its = {};
- its.it_value = absl::ToTimespec(absl::Seconds(1));
- DisableSave ds; // Asserting an EINTR.
- ASSERT_NO_ERRNO(timer.Set(0, its));
-
- // Sendfile from tempfd to the eventfd. Since the eventfd is not already at
- // its maximum value, the eventfd is "ready for writing"; however, since the
- // eventfd's existing value plus the new value would exceed the maximum, the
- // write should internally fail with EWOULDBLOCK. In this case, sendfile()
- // should block instead of spinning, and eventually be interrupted by our
- // timer. See b/172075629.
- EXPECT_THAT(
- sendfile(efd.get(), tempfd.get(), nullptr, sizeof(kMaxEventfdValue)),
- SyscallFailsWithErrno(EINTR));
-
- // Signal should have been handled.
- EXPECT_EQ(signaled, 1);
-}
-
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/sendfile_socket.cc b/test/syscalls/linux/sendfile_socket.cc
index c101fe9d2..ac6e89e91 100644
--- a/test/syscalls/linux/sendfile_socket.cc
+++ b/test/syscalls/linux/sendfile_socket.cc
@@ -24,8 +24,8 @@
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/socket.cc b/test/syscalls/linux/socket.cc
index 7b966484d..d2762b6e9 100644
--- a/test/syscalls/linux/socket.cc
+++ b/test/syscalls/linux/socket.cc
@@ -20,8 +20,8 @@
#include <unistd.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_umask.h"
#include "test/util/test_util.h"
@@ -119,6 +119,9 @@ TEST(SocketTest, UnixSCMRightsOnlyPassedOnce) {
// Send more than what will fit inside the send/receive buffers, so that it is
// split into multiple messages.
constexpr int kBufSize = 0x100000;
+ // Heap allocation is async-signal-unsafe and thus cannot occur between fork()
+ // and execve().
+ std::vector<char> buf(kBufSize);
pid_t pid = fork();
if (pid == 0) {
@@ -127,7 +130,6 @@ TEST(SocketTest, UnixSCMRightsOnlyPassedOnce) {
// Construct a message with some control message.
struct msghdr msg = {};
char control[CMSG_SPACE(sizeof(int))] = {};
- std::vector<char> buf(kBufSize);
struct iovec iov = {};
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
@@ -154,7 +156,6 @@ TEST(SocketTest, UnixSCMRightsOnlyPassedOnce) {
struct msghdr msg = {};
char control[CMSG_SPACE(sizeof(int))] = {};
- std::vector<char> buf(kBufSize);
struct iovec iov = {};
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
diff --git a/test/syscalls/linux/socket_abstract.cc b/test/syscalls/linux/socket_abstract.cc
index 00999f192..d450fad14 100644
--- a/test/syscalls/linux/socket_abstract.cc
+++ b/test/syscalls/linux/socket_abstract.cc
@@ -15,10 +15,10 @@
#include <vector>
#include "test/syscalls/linux/socket_generic.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix.h"
#include "test/syscalls/linux/socket_unix_cmsg.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_bind_to_device.cc b/test/syscalls/linux/socket_bind_to_device.cc
index 6b27f6eab..dac31a90c 100644
--- a/test/syscalls/linux/socket_bind_to_device.cc
+++ b/test/syscalls/linux/socket_bind_to_device.cc
@@ -34,8 +34,8 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_bind_to_device_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/socket_bind_to_device_distribution.cc b/test/syscalls/linux/socket_bind_to_device_distribution.cc
index 3b108cbd3..4cddb875a 100644
--- a/test/syscalls/linux/socket_bind_to_device_distribution.cc
+++ b/test/syscalls/linux/socket_bind_to_device_distribution.cc
@@ -35,8 +35,8 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_bind_to_device_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -77,34 +77,6 @@ class BindToDeviceDistributionTest
}
};
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
// Binds sockets to different devices and then creates many TCP connections.
// Checks that the distribution of connections received on the sockets matches
// the expectation.
diff --git a/test/syscalls/linux/socket_bind_to_device_sequence.cc b/test/syscalls/linux/socket_bind_to_device_sequence.cc
index d3cc71dbf..334b46730 100644
--- a/test/syscalls/linux/socket_bind_to_device_sequence.cc
+++ b/test/syscalls/linux/socket_bind_to_device_sequence.cc
@@ -36,8 +36,8 @@
#include "absl/container/node_hash_map.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_bind_to_device_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/socket_blocking.cc b/test/syscalls/linux/socket_blocking.cc
index 7e88aa2d9..5262e9ed9 100644
--- a/test/syscalls/linux/socket_blocking.cc
+++ b/test/syscalls/linux/socket_blocking.cc
@@ -23,8 +23,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
#include "test/util/timer_util.h"
diff --git a/test/syscalls/linux/socket_blocking.h b/test/syscalls/linux/socket_blocking.h
index db26e5ef5..89134ec30 100644
--- a/test/syscalls/linux/socket_blocking.h
+++ b/test/syscalls/linux/socket_blocking.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_BLOCKING_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_BLOCKING_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_capability.cc b/test/syscalls/linux/socket_capability.cc
index f75482aba..95cf1f6b4 100644
--- a/test/syscalls/linux/socket_capability.cc
+++ b/test/syscalls/linux/socket_capability.cc
@@ -16,9 +16,9 @@
// headers).
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_filesystem.cc b/test/syscalls/linux/socket_filesystem.cc
index 287359363..a611e9f4e 100644
--- a/test/syscalls/linux/socket_filesystem.cc
+++ b/test/syscalls/linux/socket_filesystem.cc
@@ -15,10 +15,10 @@
#include <vector>
#include "test/syscalls/linux/socket_generic.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix.h"
#include "test/syscalls/linux/socket_unix_cmsg.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_generic.h b/test/syscalls/linux/socket_generic.h
index 00ae7bfc3..a13262355 100644
--- a/test/syscalls/linux/socket_generic.h
+++ b/test/syscalls/linux/socket_generic.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_GENERIC_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_generic_stress.cc b/test/syscalls/linux/socket_generic_stress.cc
index c35aa2183..9ff385b41 100644
--- a/test/syscalls/linux/socket_generic_stress.cc
+++ b/test/syscalls/linux/socket_generic_stress.cc
@@ -29,57 +29,19 @@
#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/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
-constexpr char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
-
-PosixErrorOr<int> NumPorts() {
- int min = 0;
- int max = 1 << 16;
-
- // Read the ephemeral range from /proc.
- ASSIGN_OR_RETURN_ERRNO(std::string rangefile, GetContents(kRangeFile));
- const std::string err_msg =
- absl::StrFormat("%s has invalid content: %s", kRangeFile, rangefile);
- if (rangefile.back() != '\n') {
- return PosixError(EINVAL, err_msg);
- }
- rangefile.pop_back();
- std::vector<std::string> range =
- absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
- if (range.size() < 2 || !absl::SimpleAtoi(range.front(), &min) ||
- !absl::SimpleAtoi(range.back(), &max)) {
- return PosixError(EINVAL, err_msg);
- }
-
- // If we can open as writable, limit the range.
- if (!access(kRangeFile, W_OK)) {
- ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd,
- Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
- max = min + 50;
- const std::string small_range = absl::StrFormat("%d %d", min, max);
- int n = write(fd.get(), small_range.c_str(), small_range.size());
- if (n < 0) {
- return PosixError(
- errno,
- absl::StrFormat("write(%d [%s], \"%s\", %d)", fd.get(), kRangeFile,
- small_range.c_str(), small_range.size()));
- }
- }
- return max - min;
-}
-
// Test fixture for tests that apply to pairs of connected sockets.
using ConnectStressTest = SocketPairTest;
TEST_P(ConnectStressTest, Reset) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
const std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@@ -103,7 +65,7 @@ TEST_P(ConnectStressTest, Reset) {
// Tests that opening too many connections -- without closing them -- does lead
// to port exhaustion.
TEST_P(ConnectStressTest, TooManyOpen) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
int err_num = 0;
std::vector<std::unique_ptr<SocketPair>> sockets =
std::vector<std::unique_ptr<SocketPair>>(nports);
@@ -164,7 +126,7 @@ class PersistentListenerConnectStressTest : public SocketPairTest {
};
TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
@@ -185,7 +147,7 @@ TEST_P(PersistentListenerConnectStressTest, ShutdownCloseFirst) {
}
TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
const std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
@@ -206,7 +168,7 @@ TEST_P(PersistentListenerConnectStressTest, ShutdownCloseSecond) {
}
TEST_P(PersistentListenerConnectStressTest, Close) {
- const int nports = ASSERT_NO_ERRNO_AND_VALUE(NumPorts());
+ const int nports = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
for (int i = 0; i < nports * 2; i++) {
std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketSleep());
diff --git a/test/syscalls/linux/socket_generic_test_cases.cc b/test/syscalls/linux/socket_generic_test_cases.cc
index 5c4cb6c35..c509d54e2 100644
--- a/test/syscalls/linux/socket_generic_test_cases.cc
+++ b/test/syscalls/linux/socket_generic_test_cases.cc
@@ -14,6 +14,9 @@
#include "test/syscalls/linux/socket_generic.h"
+#ifdef __linux__
+#include <linux/capability.h>
+#endif // __linux__
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -22,8 +25,9 @@
#include "gtest/gtest.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/capability_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// This file is a generic socket test file. It must be built with another file
@@ -400,6 +404,46 @@ TEST_P(AllSocketPairTest, RcvBufSucceeds) {
EXPECT_GT(size, 0);
}
+#ifdef __linux__
+
+// Check that setting SO_RCVBUFFORCE above max is not clamped to the maximum
+// receive buffer size.
+TEST_P(AllSocketPairTest, SetSocketRecvBufForceAboveMax) {
+ std::unique_ptr<SocketPair> sockets =
+ ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ // Discover maxmimum buffer size by setting to a really large value.
+ constexpr int kRcvBufSz = 0xffffffff;
+ ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz,
+ sizeof(kRcvBufSz)),
+ SyscallSucceeds());
+
+ int max = 0;
+ socklen_t max_len = sizeof(max);
+ ASSERT_THAT(
+ getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &max, &max_len),
+ SyscallSucceeds());
+
+ int above_max = max + 1;
+ int sso = setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUFFORCE,
+ &above_max, sizeof(above_max));
+ if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))) {
+ ASSERT_THAT(sso, SyscallFailsWithErrno(EPERM));
+ return;
+ }
+ ASSERT_THAT(sso, SyscallSucceeds());
+
+ int val = 0;
+ socklen_t val_len = sizeof(val);
+ ASSERT_THAT(
+ getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &val, &val_len),
+ SyscallSucceeds());
+ // The system doubles the passed-in maximum.
+ ASSERT_EQ(above_max * 2, val);
+}
+
+#endif // __linux__
+
TEST_P(AllSocketPairTest, GetSndBufSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int size = 0;
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index a3bdada86..13a83a1b3 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -34,10 +34,11 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/syscalls/linux/socket_inet_loopback_test_params.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
#include "test/util/save_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -48,45 +49,7 @@ namespace {
using ::testing::Gt;
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-struct TestParam {
- TestAddress listener;
- TestAddress connector;
-};
-
-std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) {
- return absl::StrCat("Listen", info.param.listener.description, "_Connect",
- info.param.connector.description);
-}
-
-using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
+using SocketInetLoopbackTest = ::testing::TestWithParam<SocketInetTestParam>;
TEST(BadSocketPairArgs, ValidateErrForBadCallsToSocketPair) {
int fd[2] = {};
@@ -299,7 +262,7 @@ void tcpSimpleConnectTest(TestAddress const& listener,
}
TEST_P(SocketInetLoopbackTest, TCP) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -307,7 +270,7 @@ TEST_P(SocketInetLoopbackTest, TCP) {
}
TEST_P(SocketInetLoopbackTest, TCPListenUnbound) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -316,7 +279,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenUnbound) {
}
TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
- const auto& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
const TestAddress& listener = param.listener;
const TestAddress& connector = param.connector;
@@ -345,7 +308,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
sockaddr_storage conn_addr = connector.addr;
ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- // TODO(b/157236388): Remove Disable save after bug is fixed. S/R test can
+ // TODO(b/153489135): Remove Disable save after bug is fixed. S/R test can
// fail because the last socket may not be delivered to the accept queue
// by the time connect returns.
DisableSave ds;
@@ -362,13 +325,14 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
}
TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
constexpr int kBacklog = 2;
- constexpr int kFDs = kBacklog + 1;
+ // See the comment in TCPBacklog for why this isn't kBacklog + 1.
+ constexpr int kFDs = kBacklog;
// Create the listening socket.
FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -429,7 +393,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
}
TEST_P(SocketInetLoopbackTest, TCPListenClose) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -454,6 +418,8 @@ TEST_P(SocketInetLoopbackTest, TCPListenClose) {
uint16_t const port =
ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+ // Connect repeatedly, keeping each connection open. After kBacklog
+ // connections, we'll start getting EINPROGRESS.
sockaddr_storage conn_addr = connector.addr;
ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
std::vector<FileDescriptor> clients;
@@ -474,7 +440,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenClose) {
// Test the protocol state information returned by TCPINFO.
TEST_P(SocketInetLoopbackTest, TCPInfoState) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -543,7 +509,7 @@ TEST_P(SocketInetLoopbackTest, TCPInfoState) {
ASSERT_THAT(close(conn_fd.release()), SyscallSucceeds());
}
-void TestHangupDuringConnect(const TestParam& param,
+void TestHangupDuringConnect(const SocketInetTestParam& param,
void (*hangup)(FileDescriptor&)) {
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -606,8 +572,10 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownDuringConnect) {
});
}
-void TestListenHangupConnectingRead(const TestParam& param,
+void TestListenHangupConnectingRead(const SocketInetTestParam& param,
void (*hangup)(FileDescriptor&)) {
+ constexpr int kTimeout = 10000;
+
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -637,14 +605,33 @@ void TestListenHangupConnectingRead(const TestParam& param,
sockaddr_storage conn_addr = connector.addr;
ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
FileDescriptor established_client = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(connect(established_client.get(), AsSockAddr(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
+ Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
+ int ret = connect(established_client.get(), AsSockAddr(&conn_addr),
+ connector.addr_len);
+ if (ret != 0) {
+ EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS));
+ }
- // Ensure that the accept queue has the completed connection.
- constexpr int kTimeout = 10000;
+ // On some kernels a backlog of 0 means no backlog, while on others it means a
+ // backlog of 1. See commit c609e6aae4efcf383fe86b195d1b060befcb3666 for more
+ // explanation.
+ //
+ // If we timeout connecting to loopback, we're on a kernel with no backlog.
pollfd pfd = {
+ .fd = established_client.get(),
+ .events = POLLIN | POLLOUT,
+ };
+ if (!poll(&pfd, 1, kTimeout)) {
+ // We're on one of those kernels. It should be impossible to establish the
+ // connection, so connect will always return EALREADY.
+ EXPECT_THAT(connect(established_client.get(), AsSockAddr(&conn_addr),
+ connector.addr_len),
+ SyscallFailsWithErrno(EALREADY));
+ return;
+ }
+
+ // Ensure that the accept queue has the completed connection.
+ pfd = {
.fd = listen_fd.get(),
.events = POLLIN,
};
@@ -654,8 +641,8 @@ void TestListenHangupConnectingRead(const TestParam& param,
FileDescriptor connecting_client = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
// Keep the last client in connecting state.
- int ret = connect(connecting_client.get(), AsSockAddr(&conn_addr),
- connector.addr_len);
+ ret = connect(connecting_client.get(), AsSockAddr(&conn_addr),
+ connector.addr_len);
if (ret != 0) {
EXPECT_THAT(ret, SyscallFailsWithErrno(EINPROGRESS));
}
@@ -694,7 +681,7 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownConnectingRead) {
// Test close of a non-blocking connecting socket.
TEST_P(SocketInetLoopbackTest, TCPNonBlockingConnectClose) {
- TestParam const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -720,7 +707,7 @@ TEST_P(SocketInetLoopbackTest, TCPNonBlockingConnectClose) {
// Try many iterations to catch a race with socket close and handshake
// completion.
- for (int i = 0; i < 1000; ++i) {
+ for (int i = 0; i < 100; ++i) {
FileDescriptor client = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
ASSERT_THAT(
@@ -764,12 +751,12 @@ TEST_P(SocketInetLoopbackTest, TCPNonBlockingConnectClose) {
}
}
-// TODO(b/157236388): Remove once bug is fixed. Test fails w/
+// TODO(b/153489135): Remove once bug is fixed. Test fails w/
// random save as established connections which can't be delivered to the accept
// queue because the queue is full are not correctly delivered after restore
// causing the last accept to timeout on the restore.
TEST_P(SocketInetLoopbackTest, TCPAcceptBacklogSizes) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -795,7 +782,8 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptBacklogSizes) {
if (backlog < 0) {
expected_accepts = 1024;
} else {
- expected_accepts = backlog + 1;
+ // See the comment in TCPBacklog for why this isn't backlog + 1.
+ expected_accepts = backlog;
}
for (int i = 0; i < expected_accepts; i++) {
SCOPED_TRACE(absl::StrCat("i=", i));
@@ -813,12 +801,12 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptBacklogSizes) {
}
}
-// TODO(b/157236388): Remove once bug is fixed. Test fails w/
+// TODO(b/153489135): Remove once bug is fixed. Test fails w/
// random save as established connections which can't be delivered to the accept
// queue because the queue is full are not correctly delivered after restore
// causing the last accept to timeout on the restore.
TEST_P(SocketInetLoopbackTest, TCPBacklog) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -896,16 +884,20 @@ TEST_P(SocketInetLoopbackTest, TCPBacklog) {
// enqueuing established connections to the accept queue, newer SYNs could
// still be replied to causing those client connections would be accepted as
// we start dequeuing the queue.
- ASSERT_GE(accepted_conns, kBacklogSize + 1);
+ //
+ // On some kernels this can value can be off by one, so we don't add 1 to
+ // kBacklogSize. See commit c609e6aae4efcf383fe86b195d1b060befcb3666 for more
+ // explanation.
+ ASSERT_GE(accepted_conns, kBacklogSize);
ASSERT_GE(client_conns, accepted_conns);
}
-// TODO(b/157236388): Remove once bug is fixed. Test fails w/
+// TODO(b/153489135): Remove once bug is fixed. Test fails w/
// random save as established connections which can't be delivered to the accept
// queue because the queue is full are not correctly delivered after restore
// causing the last accept to timeout on the restore.
TEST_P(SocketInetLoopbackTest, TCPBacklogAcceptAll) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -931,7 +923,9 @@ TEST_P(SocketInetLoopbackTest, TCPBacklogAcceptAll) {
// Fill up the accept queue and trigger more client connections which would be
// waiting to be accepted.
- std::array<FileDescriptor, kBacklog + 1> established_clients;
+ //
+ // See the comment in TCPBacklog for why this isn't backlog + 1.
+ std::array<FileDescriptor, kBacklog> established_clients;
for (auto& fd : established_clients) {
fd = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
@@ -993,175 +987,12 @@ TEST_P(SocketInetLoopbackTest, TCPBacklogAcceptAll) {
}
}
-// TCPFinWait2Test creates a pair of connected sockets then closes one end to
-// trigger FIN_WAIT2 state for the closed endpoint. Then it binds the same local
-// IP/port on a new socket and tries to connect. The connect should fail w/
-// an EADDRINUSE. Then we wait till the FIN_WAIT2 timeout is over and try the
-// connect again with a new socket and this time it should succeed.
-//
-// TCP timers are not S/R today, this can cause this test to be flaky when run
-// under random S/R due to timer being reset on a restore.
-TEST_P(SocketInetLoopbackTest, TCPFinWait2Test) {
- 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(), AsSockAddr(&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(), AsSockAddr(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // Lower FIN_WAIT2 state to 5 seconds for test.
- constexpr int kTCPLingerTimeout = 5;
- EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
- &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
- SyscallSucceedsWithValue(0));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
- SyscallSucceeds());
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- // Now bind and connect a new socket.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- // Disable cooperative saves after this point. As a save between the first
- // bind/connect and the second one can cause the linger timeout timer to
- // be restarted causing the final bind/connect to fail.
- DisableSave ds;
-
- ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
- SyscallFailsWithErrno(EADDRINUSE));
-
- // Sleep for a little over the linger timeout to reduce flakiness in
- // save/restore tests.
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2));
-
- ds.reset();
-
- ASSERT_THAT(
- RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
- SyscallSucceeds());
-}
-
-// TCPLinger2TimeoutAfterClose creates a pair of connected sockets
-// then closes one end to trigger FIN_WAIT2 state for the closed endpont.
-// It then sleeps for the TCP_LINGER2 timeout and verifies that bind/
-// connecting the same address succeeds.
-//
-// TCP timers are not S/R today, this can cause this test to be flaky when run
-// under random S/R due to timer being reset on a restore.
-TEST_P(SocketInetLoopbackTest, TCPLinger2TimeoutAfterClose) {
- 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(), AsSockAddr(&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(), AsSockAddr(&listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- sockaddr_storage conn_addr = connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- connector.addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- sockaddr_storage conn_bound_addr;
- socklen_t conn_addrlen = connector.addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
- SyscallSucceeds());
-
- // Disable cooperative saves after this point as TCP timers are not restored
- // across a S/R.
- {
- DisableSave ds;
- constexpr int kTCPLingerTimeout = 5;
- EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
- &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
- SyscallSucceedsWithValue(0));
-
- // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
- conn_fd.reset();
-
- absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1));
-
- // ds going out of scope will Re-enable S/R's since at this point the timer
- // must have fired and cleaned up the endpoint.
- }
-
- // Now bind and connect a new socket and verify that we can immediately
- // rebind the address bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
- SyscallSucceeds());
- ASSERT_THAT(
- RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
- SyscallSucceeds());
-}
-
// TCPResetAfterClose creates a pair of connected sockets then closes
// one end to trigger FIN_WAIT2 state for the closed endpoint verifies
// that we generate RSTs for any new data after the socket is fully
// closed.
TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1221,198 +1052,8 @@ TEST_P(SocketInetLoopbackTest, TCPResetAfterClose) {
SyscallSucceedsWithValue(0));
}
-// setupTimeWaitClose sets up a socket endpoint in TIME_WAIT state.
-// Callers can choose to perform active close on either ends of the connection
-// and also specify if they want to enabled SO_REUSEADDR.
-void setupTimeWaitClose(const TestAddress* listener,
- const TestAddress* connector, bool reuse,
- bool accept_close, sockaddr_storage* listen_addr,
- sockaddr_storage* conn_bound_addr) {
- // Create the listening socket.
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(listener->family(), SOCK_STREAM, IPPROTO_TCP));
- if (reuse) {
- ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- }
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(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(), AsSockAddr(listen_addr), &addrlen),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener->family(), *listen_addr));
-
- // Connect to the listening socket.
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(connector->family(), SOCK_STREAM, IPPROTO_TCP));
-
- // We disable saves after this point as a S/R causes the netstack seed
- // to be regenerated which changes what ports/ISN is picked for a given
- // tuple (src ip,src port, dst ip, dst port). This can cause the final
- // SYN to use a sequence number that looks like one from the current
- // connection in TIME_WAIT and will not be accepted causing the test
- // to timeout.
- //
- // TODO(gvisor.dev/issue/940): S/R portSeed/portHint
- DisableSave ds;
-
- sockaddr_storage conn_addr = connector->addr;
- ASSERT_NO_ERRNO(SetAddrPort(connector->family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- connector->addr_len),
- SyscallSucceeds());
-
- // Accept the connection.
- auto accepted =
- ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
-
- // Get the address/port bound by the connecting socket.
- socklen_t conn_addrlen = connector->addr_len;
- ASSERT_THAT(
- getsockname(conn_fd.get(), AsSockAddr(conn_bound_addr), &conn_addrlen),
- SyscallSucceeds());
-
- FileDescriptor active_closefd, passive_closefd;
- if (accept_close) {
- active_closefd = std::move(accepted);
- passive_closefd = std::move(conn_fd);
- } else {
- active_closefd = std::move(conn_fd);
- passive_closefd = std::move(accepted);
- }
-
- // shutdown to trigger TIME_WAIT.
- ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds());
- {
- constexpr int kTimeout = 10000;
- pollfd pfd = {
- .fd = passive_closefd.get(),
- .events = POLLIN,
- };
- ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
- ASSERT_EQ(pfd.revents, POLLIN);
- }
- ASSERT_THAT(shutdown(passive_closefd.get(), SHUT_WR), SyscallSucceeds());
- {
- constexpr int kTimeout = 10000;
- constexpr int16_t want_events = POLLHUP;
- pollfd pfd = {
- .fd = active_closefd.get(),
- .events = want_events,
- };
- ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
- }
-
- // This sleep is needed to reduce flake to ensure that the passive-close
- // ensures the state transitions to CLOSE from LAST_ACK.
- absl::SleepFor(absl::Seconds(1));
-}
-
-// These tests are disabled under random save as the the restore run
-// results in the stack.Seed() being different which can cause
-// sequence number of final connect to be one that is considered
-// old and can cause the test to be flaky.
-//
-// Test re-binding of client and server bound addresses when the older
-// connection is in TIME_WAIT.
-TEST_P(SocketInetLoopbackTest, TCPPassiveCloseNoTimeWaitTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
- true /*accept_close*/, &listen_addr, &conn_bound_addr);
-
- // Now bind a new socket and verify that we can immediately rebind the address
- // bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallSucceeds());
-
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
-TEST_P(SocketInetLoopbackTest, TCPPassiveCloseNoTimeWaitReuseTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
- true /*accept_close*/, &listen_addr, &conn_bound_addr);
-
- FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(
- bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
-
- // Now bind and connect new socket and verify that we can immediately rebind
- // the address bound by the conn_fd as it never entered TIME_WAIT.
- const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallSucceeds());
-
- uint16_t const port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(param.listener.family(), listen_addr));
- sockaddr_storage conn_addr = param.connector.addr;
- ASSERT_NO_ERRNO(SetAddrPort(param.connector.family(), &conn_addr, port));
- ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
- param.connector.addr_len),
- SyscallSucceeds());
-}
-
-TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
- false /*accept_close*/, &listen_addr, &conn_bound_addr);
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
-
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
-TEST_P(SocketInetLoopbackTest, TCPActiveCloseTimeWaitReuseTest) {
- auto const& param = GetParam();
- sockaddr_storage listen_addr, conn_bound_addr;
- listen_addr = param.listener.addr;
- setupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
- false /*accept_close*/, &listen_addr, &conn_bound_addr);
- FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
- Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
- param.connector.addr_len),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1464,7 +1105,7 @@ TEST_P(SocketInetLoopbackTest, AcceptedInheritsTCPUserTimeout) {
}
TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1495,7 +1136,7 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) {
sockaddr_storage conn_addr = connector.addr;
ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
- // TODO(b/157236388): Reenable Cooperative S/R once bug is fixed.
+ // TODO(b/153489135): Reenable Cooperative S/R once bug is fixed.
DisableSave ds;
ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
connector.addr_len),
@@ -1575,7 +1216,7 @@ TEST_P(SocketInetLoopbackTest, TCPDeferAccept) {
// saved. Enable S/R issue is fixed.
DisableSave ds;
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1655,7 +1296,7 @@ TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout) {
// saved. Enable S/R once issue is fixed.
DisableSave ds;
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1722,42 +1363,16 @@ TEST_P(SocketInetLoopbackTest, TCPDeferAcceptTimeout) {
ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
}
-INSTANTIATE_TEST_SUITE_P(
- All, SocketInetLoopbackTest,
- ::testing::Values(
- // Listeners bound to IPv4 addresses refuse connections using IPv6
- // addresses.
- TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()},
- TestParam{V4Any(), V4MappedAny()},
- TestParam{V4Any(), V4MappedLoopback()},
- TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
- TestParam{V4MappedAny(), V4Any()},
- TestParam{V4MappedAny(), V4Loopback()},
- TestParam{V4MappedAny(), V4MappedAny()},
- TestParam{V4MappedAny(), V4MappedLoopback()},
- TestParam{V4MappedLoopback(), V4Any()},
- TestParam{V4MappedLoopback(), V4Loopback()},
- TestParam{V4MappedLoopback(), V4MappedLoopback()},
-
- // Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()},
- TestParam{V6Any(), V4MappedAny()},
- TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()},
- TestParam{V6Any(), V6Loopback()},
-
- // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
- // addresses.
- TestParam{V6Loopback(), V6Any()},
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
+INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackTest,
+ SocketInetLoopbackTestValues(),
+ DescribeSocketInetTestParam);
-using SocketInetReusePortTest = ::testing::TestWithParam<TestParam>;
+using SocketInetReusePortTest = ::testing::TestWithParam<SocketInetTestParam>;
// TODO(gvisor.dev/issue/940): Remove when portHint/stack.Seed is
// saved/restored.
TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1867,7 +1482,7 @@ TEST_P(SocketInetReusePortTest, TcpPortReuseMultiThread) {
}
TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -1978,7 +1593,7 @@ TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThread) {
}
TEST_P(SocketInetReusePortTest, UdpPortReuseMultiThreadShort) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -2086,32 +1701,23 @@ INSTANTIATE_TEST_SUITE_P(
::testing::Values(
// Listeners bound to IPv4 addresses refuse connections using IPv6
// addresses.
- TestParam{V4Any(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
+ SocketInetTestParam{V4Any(), V4Loopback()},
+ SocketInetTestParam{V4Loopback(), V4MappedLoopback()},
// Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Loopback()}, TestParam{V6Any(), V6Loopback()},
+ SocketInetTestParam{V6Any(), V4Loopback()},
+ SocketInetTestParam{V6Any(), V6Loopback()},
// Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
// addresses.
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-struct ProtocolTestParam {
- std::string description;
- int type;
-};
-
-std::string DescribeProtocolTestParam(
- ::testing::TestParamInfo<ProtocolTestParam> const& info) {
- return info.param.description;
-}
+ SocketInetTestParam{V6Loopback(), V6Loopback()}),
+ DescribeSocketInetTestParam);
using SocketMultiProtocolInetLoopbackTest =
::testing::TestWithParam<ProtocolTestParam>;
TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 loopback on a dual stack socket.
@@ -2160,7 +1766,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedLoopbackOnlyReservesV4) {
}
TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 any on a dual stack socket.
@@ -2209,7 +1815,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedAnyOnlyReservesV4) {
}
TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Bind the v6 any on a dual stack socket.
TestAddress const& test_addr_dual = V6Any();
@@ -2272,7 +1878,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, DualStackV6AnyReservesEverything) {
TEST_P(SocketMultiProtocolInetLoopbackTest,
DualStackV6AnyReuseAddrDoesNotReserveV4Any) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Bind the v6 any on a dual stack socket.
TestAddress const& test_addr_dual = V6Any();
@@ -2309,7 +1915,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
TEST_P(SocketMultiProtocolInetLoopbackTest,
DualStackV6AnyReuseAddrListenReservesV4Any) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Only TCP sockets are supported.
SKIP_IF((param.type & SOCK_STREAM) == 0);
@@ -2352,7 +1958,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
TEST_P(SocketMultiProtocolInetLoopbackTest,
DualStackV6AnyWithListenReservesEverything) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// Only TCP sockets are supported.
SKIP_IF((param.type & SOCK_STREAM) == 0);
@@ -2419,7 +2025,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
}
TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v6 any on a v6-only socket.
@@ -2472,7 +2078,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6OnlyV6AnyReservesV6) {
}
TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v6 loopback on a dual stack socket.
@@ -2552,66 +2158,8 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) {
}
}
-TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReservedReuseAddr) {
- auto const& param = GetParam();
-
- // Bind the v6 loopback on a dual stack socket.
- TestAddress const& test_addr = V6Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
- SyscallSucceeds());
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
- SyscallSucceeds());
-}
-
TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 loopback on a dual stack socket.
@@ -2723,68 +2271,8 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) {
}
}
-TEST_P(SocketMultiProtocolInetLoopbackTest,
- V4MappedEphemeralPortReservedResueAddr) {
- auto const& param = GetParam();
-
- // Bind the v4 loopback on a dual stack socket.
- TestAddress const& test_addr = V4MappedLoopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
- SyscallSucceeds());
-
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
- SyscallSucceeds());
-}
-
TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
for (int i = 0; true; i++) {
// Bind the v4 loopback on a v4 socket.
@@ -2897,71 +2385,9 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) {
}
}
-TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReservedReuseAddr) {
- auto const& param = GetParam();
-
- // Bind the v4 loopback on a v4 socket.
- TestAddress const& test_addr = V4Loopback();
- sockaddr_storage bound_addr = test_addr.addr;
- const FileDescriptor bound_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
-
- ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
- sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
- SyscallSucceeds());
-
- // Listen iff TCP.
- if (param.type == SOCK_STREAM) {
- ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
- }
-
- // Get the port that we bound.
- socklen_t bound_addr_len = test_addr.addr_len;
- ASSERT_THAT(
- getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
- SyscallSucceeds());
-
- // Connect to bind an ephemeral port.
- const FileDescriptor connected_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
-
- ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
-
- ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
-
- // Get the ephemeral port.
- sockaddr_storage connected_addr = {};
- socklen_t connected_addr_len = sizeof(connected_addr);
- ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
- &connected_addr_len),
- SyscallSucceeds());
- uint16_t const ephemeral_port =
- ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
-
- // Verify that we actually got an ephemeral port.
- ASSERT_NE(ephemeral_port, 0);
-
- // Verify that the ephemeral port is not reserved.
- const FileDescriptor checking_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
- &kSockOptOn, sizeof(kSockOptOn)),
- SyscallSucceeds());
- EXPECT_THAT(
- bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
- SyscallSucceeds());
-}
-
TEST_P(SocketMultiProtocolInetLoopbackTest,
MultipleBindsAllowedNoListeningReuseAddr) {
- const auto& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
// UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP
// this is only permitted if there is no other listening socket.
SKIP_IF(param.type != SOCK_STREAM);
@@ -2996,7 +2422,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
}
TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
TestAddress const& test_addr = V4Loopback();
sockaddr_storage addr = test_addr.addr;
@@ -3049,7 +2475,7 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, PortReuseTwoSockets) {
// closed, we can bind a different socket to the same address without needing
// REUSEPORT.
TEST_P(SocketMultiProtocolInetLoopbackTest, NoReusePortFollowingReusePort) {
- auto const& param = GetParam();
+ ProtocolTestParam const& param = GetParam();
TestAddress const& test_addr = V4Loopback();
sockaddr_storage addr = test_addr.addr;
@@ -3076,11 +2502,8 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, NoReusePortFollowingReusePort) {
ASSERT_THAT(bind(fd, AsSockAddr(&addr), addrlen), SyscallSucceeds());
}
-INSTANTIATE_TEST_SUITE_P(
- AllFamilies, SocketMultiProtocolInetLoopbackTest,
- ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
- ProtocolTestParam{"UDP", SOCK_DGRAM}),
- DescribeProtocolTestParam);
+INSTANTIATE_TEST_SUITE_P(AllFamilies, SocketMultiProtocolInetLoopbackTest,
+ ProtocolTestValues(), DescribeProtocolTestParam);
} // namespace
diff --git a/test/syscalls/linux/socket_inet_loopback_isolated.cc b/test/syscalls/linux/socket_inet_loopback_isolated.cc
new file mode 100644
index 000000000..182d20a9e
--- /dev/null
+++ b/test/syscalls/linux/socket_inet_loopback_isolated.cc
@@ -0,0 +1,489 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <netinet/tcp.h>
+
+#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
+#include "test/syscalls/linux/socket_inet_loopback_test_params.h"
+#include "test/util/socket_util.h"
+#include "test/util/test_util.h"
+
+// Unit tests in this file will run in their own network namespace.
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+using SocketInetLoopbackIsolatedTest =
+ ::testing::TestWithParam<SocketInetTestParam>;
+
+TEST_P(SocketInetLoopbackIsolatedTest, TCPActiveCloseTimeWaitTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
+ false /*accept_close*/, &listen_addr, &conn_bound_addr);
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+TEST_P(SocketInetLoopbackIsolatedTest, TCPActiveCloseTimeWaitReuseTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
+ false /*accept_close*/, &listen_addr, &conn_bound_addr);
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+// These tests are disabled under random save as the restore run
+// results in the stack.Seed() being different which can cause
+// sequence number of final connect to be one that is considered
+// old and can cause the test to be flaky.
+//
+// Test re-binding of client and server bound addresses when the older
+// connection is in TIME_WAIT.
+TEST_P(SocketInetLoopbackIsolatedTest, TCPPassiveCloseNoTimeWaitTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, false /*reuse*/,
+ true /*accept_close*/, &listen_addr, &conn_bound_addr);
+
+ // Now bind a new socket and verify that we can immediately rebind the address
+ // bound by the conn_fd as it never entered TIME_WAIT.
+ const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallSucceeds());
+
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+TEST_P(SocketInetLoopbackIsolatedTest, TCPPassiveCloseNoTimeWaitReuseTest) {
+ SocketInetTestParam const& param = GetParam();
+ sockaddr_storage listen_addr, conn_bound_addr;
+ listen_addr = param.listener.addr;
+ SetupTimeWaitClose(&param.listener, &param.connector, true /*reuse*/,
+ true /*accept_close*/, &listen_addr, &conn_bound_addr);
+
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.listener.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(&listen_addr), param.listener.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(listen_fd.get(), SOMAXCONN), SyscallSucceeds());
+
+ // Now bind and connect new socket and verify that we can immediately rebind
+ // the address bound by the conn_fd as it never entered TIME_WAIT.
+ const FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(param.connector.family(), SOCK_STREAM, IPPROTO_TCP));
+ ASSERT_THAT(setsockopt(conn_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(bind(conn_fd.get(), AsSockAddr(&conn_bound_addr),
+ param.connector.addr_len),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(param.listener.family(), listen_addr));
+ sockaddr_storage conn_addr = param.connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(param.connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ param.connector.addr_len),
+ SyscallSucceeds());
+}
+
+// TCPFinWait2Test creates a pair of connected sockets then closes one end to
+// trigger FIN_WAIT2 state for the closed endpoint. Then it binds the same local
+// IP/port on a new socket and tries to connect. The connect should fail w/
+// an EADDRINUSE. Then we wait till the FIN_WAIT2 timeout is over and try the
+// connect again with a new socket and this time it should succeed.
+//
+// TCP timers are not S/R today, this can cause this test to be flaky when run
+// under random S/R due to timer being reset on a restore.
+TEST_P(SocketInetLoopbackIsolatedTest, TCPFinWait2Test) {
+ SocketInetTestParam 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(), AsSockAddr(&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(), AsSockAddr(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // Lower FIN_WAIT2 state to 5 seconds for test.
+ constexpr int kTCPLingerTimeout = 5;
+ EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
+ &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
+ SyscallSucceedsWithValue(0));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ sockaddr_storage conn_bound_addr;
+ socklen_t conn_addrlen = connector.addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
+ SyscallSucceeds());
+
+ // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
+ conn_fd.reset();
+
+ // Now bind and connect a new socket.
+ const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // Disable cooperative saves after this point. As a save between the first
+ // bind/connect and the second one can cause the linger timeout timer to
+ // be restarted causing the final bind/connect to fail.
+ DisableSave ds;
+
+ ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
+ SyscallFailsWithErrno(EADDRINUSE));
+
+ // Sleep for a little over the linger timeout to reduce flakiness in
+ // save/restore tests.
+ absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 2));
+
+ ds.reset();
+
+ ASSERT_THAT(
+ RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
+ SyscallSucceeds());
+}
+
+// TCPLinger2TimeoutAfterClose creates a pair of connected sockets
+// then closes one end to trigger FIN_WAIT2 state for the closed endpoint.
+// It then sleeps for the TCP_LINGER2 timeout and verifies that bind/
+// connecting the same address succeeds.
+//
+// TCP timers are not S/R today, this can cause this test to be flaky when run
+// under random S/R due to timer being reset on a restore.
+TEST_P(SocketInetLoopbackIsolatedTest, TCPLinger2TimeoutAfterClose) {
+ SocketInetTestParam 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(), AsSockAddr(&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(), AsSockAddr(&listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener.family(), listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ sockaddr_storage conn_addr = connector.addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ connector.addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ sockaddr_storage conn_bound_addr;
+ socklen_t conn_addrlen = connector.addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), AsSockAddr(&conn_bound_addr), &conn_addrlen),
+ SyscallSucceeds());
+
+ // Disable cooperative saves after this point as TCP timers are not restored
+ // across a S/R.
+ {
+ DisableSave ds;
+ constexpr int kTCPLingerTimeout = 5;
+ EXPECT_THAT(setsockopt(conn_fd.get(), IPPROTO_TCP, TCP_LINGER2,
+ &kTCPLingerTimeout, sizeof(kTCPLingerTimeout)),
+ SyscallSucceedsWithValue(0));
+
+ // close the connecting FD to trigger FIN_WAIT2 on the connected fd.
+ conn_fd.reset();
+
+ absl::SleepFor(absl::Seconds(kTCPLingerTimeout + 1));
+
+ // ds going out of scope will Re-enable S/R's since at this point the timer
+ // must have fired and cleaned up the endpoint.
+ }
+
+ // Now bind and connect a new socket and verify that we can immediately
+ // rebind the address bound by the conn_fd as it never entered TIME_WAIT.
+ const FileDescriptor conn_fd2 = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(conn_fd2.get(), AsSockAddr(&conn_bound_addr), conn_addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ RetryEINTR(connect)(conn_fd2.get(), AsSockAddr(&conn_addr), conn_addrlen),
+ SyscallSucceeds());
+}
+
+INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackIsolatedTest,
+ SocketInetLoopbackTestValues(),
+ DescribeSocketInetTestParam);
+
+using SocketMultiProtocolInetLoopbackIsolatedTest =
+ ::testing::TestWithParam<ProtocolTestParam>;
+
+TEST_P(SocketMultiProtocolInetLoopbackIsolatedTest,
+ V4EphemeralPortReservedReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+
+ // Bind the v4 loopback on a v4 socket.
+ TestAddress const& test_addr = V4Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ const FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+
+ // Listen iff TCP.
+ if (param.type == SOCK_STREAM) {
+ ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
+ }
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+
+ // Connect to bind an ephemeral port.
+ const FileDescriptor connected_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
+
+ // Get the ephemeral port.
+ sockaddr_storage connected_addr = {};
+ socklen_t connected_addr_len = sizeof(connected_addr);
+ ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
+ &connected_addr_len),
+ SyscallSucceeds());
+ uint16_t const ephemeral_port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
+
+ // Verify that we actually got an ephemeral port.
+ ASSERT_NE(ephemeral_port, 0);
+
+ // Verify that the ephemeral port is not reserved.
+ const FileDescriptor checking_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ EXPECT_THAT(
+ bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
+ SyscallSucceeds());
+}
+
+TEST_P(SocketMultiProtocolInetLoopbackIsolatedTest,
+ V4MappedEphemeralPortReservedReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+
+ // Bind the v4 loopback on a dual stack socket.
+ TestAddress const& test_addr = V4MappedLoopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ const FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ // Listen iff TCP.
+ if (param.type == SOCK_STREAM) {
+ ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
+ }
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+
+ // Connect to bind an ephemeral port.
+ const FileDescriptor connected_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
+
+ // Get the ephemeral port.
+ sockaddr_storage connected_addr = {};
+ socklen_t connected_addr_len = sizeof(connected_addr);
+ ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
+ &connected_addr_len),
+ SyscallSucceeds());
+ uint16_t const ephemeral_port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
+
+ // Verify that we actually got an ephemeral port.
+ ASSERT_NE(ephemeral_port, 0);
+
+ // Verify that the ephemeral port is not reserved.
+ const FileDescriptor checking_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ EXPECT_THAT(
+ bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
+ SyscallSucceeds());
+}
+
+TEST_P(SocketMultiProtocolInetLoopbackIsolatedTest,
+ V6EphemeralPortReservedReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+
+ // Bind the v6 loopback on a dual stack socket.
+ TestAddress const& test_addr = V6Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ const FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ // Listen iff TCP.
+ if (param.type == SOCK_STREAM) {
+ ASSERT_THAT(listen(bound_fd.get(), SOMAXCONN), SyscallSucceeds());
+ }
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+
+ // Connect to bind an ephemeral port.
+ const FileDescriptor connected_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(), AsSockAddr(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
+
+ // Get the ephemeral port.
+ sockaddr_storage connected_addr = {};
+ socklen_t connected_addr_len = sizeof(connected_addr);
+ ASSERT_THAT(getsockname(connected_fd.get(), AsSockAddr(&connected_addr),
+ &connected_addr_len),
+ SyscallSucceeds());
+ uint16_t const ephemeral_port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(test_addr.family(), connected_addr));
+
+ // Verify that we actually got an ephemeral port.
+ ASSERT_NE(ephemeral_port, 0);
+
+ // Verify that the ephemeral port is not reserved.
+ const FileDescriptor checking_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+ ASSERT_THAT(setsockopt(checking_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ EXPECT_THAT(
+ bind(checking_fd.get(), AsSockAddr(&connected_addr), connected_addr_len),
+ SyscallSucceeds());
+}
+
+INSTANTIATE_TEST_SUITE_P(AllFamilies,
+ SocketMultiProtocolInetLoopbackIsolatedTest,
+ ProtocolTestValues(), DescribeProtocolTestParam);
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
index 601ae107b..479162487 100644
--- a/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
+++ b/test/syscalls/linux/socket_inet_loopback_nogotsan.cc
@@ -27,10 +27,11 @@
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/syscalls/linux/socket_inet_loopback_test_params.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
#include "test/util/save_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -38,47 +39,7 @@ namespace testing {
namespace {
-using ::testing::Gt;
-
-PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
- switch (family) {
- case AF_INET:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
- case AF_INET6:
- return static_cast<uint16_t>(
- reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
- switch (family) {
- case AF_INET:
- reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
- return NoError();
- case AF_INET6:
- reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
- return NoError();
- default:
- return PosixError(EINVAL,
- absl::StrCat("unknown socket family: ", family));
- }
-}
-
-struct TestParam {
- TestAddress listener;
- TestAddress connector;
-};
-
-std::string DescribeTestParam(::testing::TestParamInfo<TestParam> const& info) {
- return absl::StrCat("Listen", info.param.listener.description, "_Connect",
- info.param.connector.description);
-}
-
-using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
+using SocketInetLoopbackTest = ::testing::TestWithParam<SocketInetTestParam>;
// This test verifies that connect returns EADDRNOTAVAIL if all local ephemeral
// ports are already in use for a given destination ip/port.
@@ -87,7 +48,7 @@ using SocketInetLoopbackTest = ::testing::TestWithParam<TestParam>;
//
// FIXME(b/162475855): This test is failing reliably.
TEST_P(SocketInetLoopbackTest, DISABLED_TestTCPPortExhaustion) {
- auto const& param = GetParam();
+ SocketInetTestParam const& param = GetParam();
TestAddress const& listener = param.listener;
TestAddress const& connector = param.connector;
@@ -136,59 +97,32 @@ TEST_P(SocketInetLoopbackTest, DISABLED_TestTCPPortExhaustion) {
}
}
-INSTANTIATE_TEST_SUITE_P(
- All, SocketInetLoopbackTest,
- ::testing::Values(
- // Listeners bound to IPv4 addresses refuse connections using IPv6
- // addresses.
- TestParam{V4Any(), V4Any()}, TestParam{V4Any(), V4Loopback()},
- TestParam{V4Any(), V4MappedAny()},
- TestParam{V4Any(), V4MappedLoopback()},
- TestParam{V4Loopback(), V4Any()}, TestParam{V4Loopback(), V4Loopback()},
- TestParam{V4Loopback(), V4MappedLoopback()},
- TestParam{V4MappedAny(), V4Any()},
- TestParam{V4MappedAny(), V4Loopback()},
- TestParam{V4MappedAny(), V4MappedAny()},
- TestParam{V4MappedAny(), V4MappedLoopback()},
- TestParam{V4MappedLoopback(), V4Any()},
- TestParam{V4MappedLoopback(), V4Loopback()},
- TestParam{V4MappedLoopback(), V4MappedLoopback()},
-
- // Listeners bound to IN6ADDR_ANY accept all connections.
- TestParam{V6Any(), V4Any()}, TestParam{V6Any(), V4Loopback()},
- TestParam{V6Any(), V4MappedAny()},
- TestParam{V6Any(), V4MappedLoopback()}, TestParam{V6Any(), V6Any()},
- TestParam{V6Any(), V6Loopback()},
-
- // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
- // addresses.
- TestParam{V6Loopback(), V6Any()},
- TestParam{V6Loopback(), V6Loopback()}),
- DescribeTestParam);
-
-struct ProtocolTestParam {
- std::string description;
- int type;
-};
-
-std::string DescribeProtocolTestParam(
- ::testing::TestParamInfo<ProtocolTestParam> const& info) {
- return info.param.description;
-}
+INSTANTIATE_TEST_SUITE_P(All, SocketInetLoopbackTest,
+ SocketInetLoopbackTestValues(),
+ DescribeSocketInetTestParam);
using SocketMultiProtocolInetLoopbackTest =
::testing::TestWithParam<ProtocolTestParam>;
-TEST_P(SocketMultiProtocolInetLoopbackTest, BindAvoidsListeningPortsReuseAddr) {
- const auto& param = GetParam();
- // UDP sockets are allowed to bind/listen on the port w/ SO_REUSEADDR, for TCP
- // this is only permitted if there is no other listening socket.
+TEST_P(SocketMultiProtocolInetLoopbackTest,
+ TCPBindAvoidsOtherBoundPortsReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+ // UDP sockets are allowed to bind/listen on an already bound port w/
+ // SO_REUSEADDR even when requesting a port from the kernel. In case of TCP
+ // rebinding is only permitted when SO_REUSEADDR is set and an explicit port
+ // is specified. When a zero port is specified to the bind() call then an
+ // already bound port will not be picked.
SKIP_IF(param.type != SOCK_STREAM);
DisableSave ds; // Too many syscalls.
// A map of port to file descriptor binding the port.
- std::map<uint16_t, FileDescriptor> listen_sockets;
+ std::map<uint16_t, FileDescriptor> bound_sockets;
+
+ // Reduce number of ephemeral ports if permitted to reduce running time of
+ // the test.
+ [[maybe_unused]] const int nports =
+ ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
// Exhaust all ephemeral ports.
while (true) {
@@ -214,19 +148,63 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, BindAvoidsListeningPortsReuseAddr) {
SyscallSucceeds());
uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
- // Newly bound port should not already be in use by a listening socket.
- ASSERT_EQ(listen_sockets.find(port), listen_sockets.end());
- auto fd = bound_fd.get();
- listen_sockets.insert(std::make_pair(port, std::move(bound_fd)));
- ASSERT_THAT(listen(fd, SOMAXCONN), SyscallSucceeds());
+ auto [iter, inserted] = bound_sockets.emplace(port, std::move(bound_fd));
+ ASSERT_TRUE(inserted);
+ }
+}
+
+TEST_P(SocketMultiProtocolInetLoopbackTest,
+ UDPBindMayBindOtherBoundPortsReuseAddr) {
+ ProtocolTestParam const& param = GetParam();
+ // UDP sockets are allowed to bind/listen on an already bound port w/
+ // SO_REUSEADDR even when requesting a port from the kernel.
+ SKIP_IF(param.type != SOCK_DGRAM);
+
+ DisableSave ds; // Too many syscalls.
+
+ // A map of port to file descriptor binding the port.
+ std::map<uint16_t, FileDescriptor> bound_sockets;
+
+ // Reduce number of ephemeral ports if permitted to reduce running time of
+ // the test.
+ [[maybe_unused]] const int nports =
+ ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
+
+ // Exhaust all ephemeral ports.
+ bool duplicate_binding = false;
+ while (true) {
+ // Bind the v4 loopback on a v4 socket.
+ TestAddress const& test_addr = V4Loopback();
+ sockaddr_storage bound_addr = test_addr.addr;
+ FileDescriptor bound_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
+
+ ASSERT_THAT(setsockopt(bound_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ ASSERT_THAT(
+ bind(bound_fd.get(), AsSockAddr(&bound_addr), test_addr.addr_len),
+ SyscallSucceeds());
+
+ // Get the port that we bound.
+ socklen_t bound_addr_len = test_addr.addr_len;
+ ASSERT_THAT(
+ getsockname(bound_fd.get(), AsSockAddr(&bound_addr), &bound_addr_len),
+ SyscallSucceeds());
+ uint16_t port = reinterpret_cast<sockaddr_in*>(&bound_addr)->sin_port;
+
+ auto [iter, inserted] = bound_sockets.emplace(port, std::move(bound_fd));
+ if (!inserted) {
+ duplicate_binding = true;
+ break;
+ }
}
+ ASSERT_TRUE(duplicate_binding);
}
-INSTANTIATE_TEST_SUITE_P(
- AllFamilies, SocketMultiProtocolInetLoopbackTest,
- ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
- ProtocolTestParam{"UDP", SOCK_DGRAM}),
- DescribeProtocolTestParam);
+INSTANTIATE_TEST_SUITE_P(AllFamilies, SocketMultiProtocolInetLoopbackTest,
+ ProtocolTestValues(), DescribeProtocolTestParam);
} // namespace
diff --git a/test/syscalls/linux/socket_inet_loopback_test_params.h b/test/syscalls/linux/socket_inet_loopback_test_params.h
new file mode 100644
index 000000000..163e595a8
--- /dev/null
+++ b/test/syscalls/linux/socket_inet_loopback_test_params.h
@@ -0,0 +1,86 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_INET_LOOPBACK_TEST_PARAMS_H_
+#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_INET_LOOPBACK_TEST_PARAMS_H_
+
+#include "gtest/gtest.h"
+#include "test/util/socket_util.h"
+
+namespace gvisor {
+namespace testing {
+
+struct SocketInetTestParam {
+ TestAddress listener;
+ TestAddress connector;
+};
+
+inline std::string DescribeSocketInetTestParam(
+ ::testing::TestParamInfo<SocketInetTestParam> const& info) {
+ return absl::StrCat("Listen", info.param.listener.description, "_Connect",
+ info.param.connector.description);
+}
+
+inline auto SocketInetLoopbackTestValues() {
+ return ::testing::Values(
+ // Listeners bound to IPv4 addresses refuse connections using IPv6
+ // addresses.
+ SocketInetTestParam{V4Any(), V4Any()},
+ SocketInetTestParam{V4Any(), V4Loopback()},
+ SocketInetTestParam{V4Any(), V4MappedAny()},
+ SocketInetTestParam{V4Any(), V4MappedLoopback()},
+ SocketInetTestParam{V4Loopback(), V4Any()},
+ SocketInetTestParam{V4Loopback(), V4Loopback()},
+ SocketInetTestParam{V4Loopback(), V4MappedLoopback()},
+ SocketInetTestParam{V4MappedAny(), V4Any()},
+ SocketInetTestParam{V4MappedAny(), V4Loopback()},
+ SocketInetTestParam{V4MappedAny(), V4MappedAny()},
+ SocketInetTestParam{V4MappedAny(), V4MappedLoopback()},
+ SocketInetTestParam{V4MappedLoopback(), V4Any()},
+ SocketInetTestParam{V4MappedLoopback(), V4Loopback()},
+ SocketInetTestParam{V4MappedLoopback(), V4MappedLoopback()},
+
+ // Listeners bound to IN6ADDR_ANY accept all connections.
+ SocketInetTestParam{V6Any(), V4Any()},
+ SocketInetTestParam{V6Any(), V4Loopback()},
+ SocketInetTestParam{V6Any(), V4MappedAny()},
+ SocketInetTestParam{V6Any(), V4MappedLoopback()},
+ SocketInetTestParam{V6Any(), V6Any()},
+ SocketInetTestParam{V6Any(), V6Loopback()},
+
+ // Listeners bound to IN6ADDR_LOOPBACK refuse connections using IPv4
+ // addresses.
+ SocketInetTestParam{V6Loopback(), V6Any()},
+ SocketInetTestParam{V6Loopback(), V6Loopback()});
+}
+
+struct ProtocolTestParam {
+ std::string description;
+ int type;
+};
+
+inline std::string DescribeProtocolTestParam(
+ ::testing::TestParamInfo<ProtocolTestParam> const& info) {
+ return info.param.description;
+}
+
+inline auto ProtocolTestValues() {
+ return ::testing::Values(ProtocolTestParam{"TCP", SOCK_STREAM},
+ ProtocolTestParam{"UDP", SOCK_DGRAM});
+}
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_INET_LOOPBACK_TEST_PARAMS_H_
diff --git a/test/syscalls/linux/socket_ip_loopback_blocking.cc b/test/syscalls/linux/socket_ip_loopback_blocking.cc
index fda252dd7..caa5c0c63 100644
--- a/test/syscalls/linux/socket_ip_loopback_blocking.cc
+++ b/test/syscalls/linux/socket_ip_loopback_blocking.cc
@@ -18,7 +18,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc
index 2f5743cda..3271263c8 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic.cc
@@ -28,7 +28,7 @@
#include "absl/memory/memory.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/socket_ip_tcp_generic.h b/test/syscalls/linux/socket_ip_tcp_generic.h
index a3eff3c73..e9e60ef4c 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic.h
+++ b/test/syscalls/linux/socket_ip_tcp_generic.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_TCP_GENERIC_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc
index 4e79d21f4..3406874b8 100644
--- a/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc
+++ b/test/syscalls/linux/socket_ip_tcp_generic_loopback.cc
@@ -18,7 +18,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ip_tcp_generic.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_tcp_loopback.cc b/test/syscalls/linux/socket_ip_tcp_loopback.cc
index 9db3037bc..0796b8634 100644
--- a/test/syscalls/linux/socket_ip_tcp_loopback.cc
+++ b/test/syscalls/linux/socket_ip_tcp_loopback.cc
@@ -16,7 +16,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_generic.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
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 f996b93d2..533ccc3ae 100644
--- a/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc
+++ b/test/syscalls/linux/socket_ip_tcp_loopback_blocking.cc
@@ -18,7 +18,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_stream_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
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 ffa377210..05fe2a738 100644
--- a/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc
+++ b/test/syscalls/linux/socket_ip_tcp_loopback_nonblock.cc
@@ -18,7 +18,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_non_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc
index f178f1af9..88adb5b1b 100644
--- a/test/syscalls/linux/socket_ip_tcp_udp_generic.cc
+++ b/test/syscalls/linux/socket_ip_tcp_udp_generic.cc
@@ -23,7 +23,7 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc
index 1694e188a..8a87f2667 100644
--- a/test/syscalls/linux/socket_ip_udp_generic.cc
+++ b/test/syscalls/linux/socket_ip_udp_generic.cc
@@ -28,7 +28,7 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_udp_generic.h b/test/syscalls/linux/socket_ip_udp_generic.h
index 106c54e9f..a3a66c768 100644
--- a/test/syscalls/linux/socket_ip_udp_generic.h
+++ b/test/syscalls/linux/socket_ip_udp_generic.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_GENERIC_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ip_udp_loopback.cc b/test/syscalls/linux/socket_ip_udp_loopback.cc
index c7fa44884..6d06bd580 100644
--- a/test/syscalls/linux/socket_ip_udp_loopback.cc
+++ b/test/syscalls/linux/socket_ip_udp_loopback.cc
@@ -18,7 +18,7 @@
#include "test/syscalls/linux/socket_generic.h"
#include "test/syscalls/linux/socket_ip_udp_generic.h"
#include "test/syscalls/linux/socket_non_stream.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
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 d6925a8df..60d02e079 100644
--- a/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc
+++ b/test/syscalls/linux/socket_ip_udp_loopback_blocking.cc
@@ -16,7 +16,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_non_stream_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
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 d675eddc6..c011e3658 100644
--- a/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc
+++ b/test/syscalls/linux/socket_ip_udp_loopback_nonblock.cc
@@ -16,7 +16,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_non_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc
index fdbb2216b..af2459a2f 100644
--- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc
+++ b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.cc
@@ -14,7 +14,7 @@
#include "test/syscalls/linux/socket_ip_udp_unbound_external_networking.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h
index e5287addb..2e8aab129 100644
--- a/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h
+++ b/test/syscalls/linux/socket_ip_udp_unbound_external_networking.h
@@ -16,7 +16,7 @@
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IP_UDP_UNBOUND_EXTERNAL_NETWORKING_H_
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ip_unbound.cc b/test/syscalls/linux/socket_ip_unbound.cc
index 029f1e872..930f19e59 100644
--- a/test/syscalls/linux/socket_ip_unbound.cc
+++ b/test/syscalls/linux/socket_ip_unbound.cc
@@ -24,7 +24,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ip_unbound_netlink.cc b/test/syscalls/linux/socket_ip_unbound_netlink.cc
index b02222999..803a3b30b 100644
--- a/test/syscalls/linux/socket_ip_unbound_netlink.cc
+++ b/test/syscalls/linux/socket_ip_unbound_netlink.cc
@@ -25,8 +25,8 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_netlink_route_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc
new file mode 100644
index 000000000..0e3e7057b
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.cc
@@ -0,0 +1,299 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h"
+
+#include "gtest/gtest.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/util/capability_util.h"
+
+namespace gvisor {
+namespace testing {
+
+void IPv4DatagramBasedUnboundSocketTest::SetUp() {
+ if (GetParam().type & SOCK_RAW) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveRawIPSocketCapability()));
+ }
+}
+
+// Check that dropping a group membership that does not exist fails.
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastInvalidDrop) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ // Unregister from a membership that we didn't have.
+ ip_mreq group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfZero) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn iface = {};
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallSucceeds());
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfInvalidNic) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn iface = {};
+ iface.imr_ifindex = -1;
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfInvalidAddr) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreq iface = {};
+ iface.imr_interface.s_addr = inet_addr("255.255.255");
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
+ sizeof(iface)),
+ SyscallFailsWithErrno(EADDRNOTAVAIL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetShort) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ // Create a valid full-sized request.
+ ip_mreqn iface = {};
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+
+ // Send an optlen of 1 to check that optlen is enforced.
+ EXPECT_THAT(
+ setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, 1),
+ SyscallFailsWithErrno(EINVAL));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfDefault) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfDefaultReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
+ // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
+ // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
+ EXPECT_EQ(size, sizeof(in_addr));
+
+ // getsockopt(IP_MULTICAST_IF) will only return the interface address which
+ // hasn't been set.
+ EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetAddrGetReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ in_addr set = {};
+ set.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
+ // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
+ // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
+ EXPECT_EQ(size, sizeof(in_addr));
+ EXPECT_EQ(get.imr_multiaddr.s_addr, set.s_addr);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetReqAddrGetReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreq set = {};
+ set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
+ // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
+ // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
+ EXPECT_EQ(size, sizeof(in_addr));
+ EXPECT_EQ(get.imr_multiaddr.s_addr, set.imr_interface.s_addr);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetNicGetReqn) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn set = {};
+ set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ ip_mreqn get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+ EXPECT_EQ(size, sizeof(in_addr));
+ EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
+ EXPECT_EQ(get.imr_address.s_addr, 0);
+ EXPECT_EQ(get.imr_ifindex, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetAddr) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ in_addr set = {};
+ set.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, set.s_addr);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetReqAddr) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreq set = {};
+ set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, set.imr_interface.s_addr);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, IpMulticastIfSetNic) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn set = {};
+ set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+ ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
+ sizeof(set)),
+ SyscallSucceeds());
+
+ in_addr get = {};
+ socklen_t size = sizeof(get);
+ ASSERT_THAT(
+ getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
+ SyscallSucceeds());
+ EXPECT_EQ(size, sizeof(get));
+ EXPECT_EQ(get.s_addr, 0);
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, TestJoinGroupNoIf) {
+ // TODO(b/185517803): Fix for native test.
+ SKIP_IF(!IsRunningOnGvisor());
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallFailsWithErrno(ENODEV));
+}
+
+TEST_P(IPv4DatagramBasedUnboundSocketTest, TestJoinGroupInvalidIf) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+
+ ip_mreqn group = {};
+ group.imr_address.s_addr = inet_addr("255.255.255");
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
+ sizeof(group)),
+ SyscallFailsWithErrno(ENODEV));
+}
+
+// Check that multiple memberships are not allowed on the same socket.
+TEST_P(IPv4DatagramBasedUnboundSocketTest, TestMultipleJoinsOnSingleSocket) {
+ auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto fd = socket1->get();
+ ip_mreqn group = {};
+ group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
+
+ EXPECT_THAT(
+ setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
+ SyscallSucceeds());
+
+ EXPECT_THAT(
+ setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
+ SyscallFailsWithErrno(EADDRINUSE));
+}
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h
new file mode 100644
index 000000000..7ab235cad
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h
@@ -0,0 +1,31 @@
+// Copyright 2019 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_DATAGRAM_BASED_SOCKET_UNBOUND_H_
+#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_DATAGRAM_BASED_SOCKET_UNBOUND_H_
+
+#include "test/util/socket_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// Test fixture for tests that apply to IPv4 datagram-based sockets.
+class IPv4DatagramBasedUnboundSocketTest : public SimpleSocketTest {
+ void SetUp() override;
+};
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_DATAGRAM_BASED_SOCKET_UNBOUND_H_
diff --git a/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc
new file mode 100644
index 000000000..9ae68e015
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound_loopback.cc
@@ -0,0 +1,28 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/socket_ipv4_datagram_based_socket_unbound.h"
+
+namespace gvisor {
+namespace testing {
+
+INSTANTIATE_TEST_SUITE_P(IPv4Sockets, IPv4DatagramBasedUnboundSocketTest,
+ ::testing::ValuesIn(ApplyVecToVec<SocketKind>(
+ {IPv4UDPUnboundSocket, IPv4RawUDPUnboundSocket},
+ AllBitwiseCombinations(List<int>{
+ 0, SOCK_NONBLOCK}))));
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
index 18be4dcc7..05773a29c 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
@@ -15,8 +15,6 @@
#include "test/syscalls/linux/socket_ipv4_udp_unbound.h"
#include <arpa/inet.h>
-#include <net/if.h>
-#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
@@ -26,10 +24,6 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
-#include "test/util/posix_error.h"
-#include "test/util/save_util.h"
-#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
@@ -140,7 +134,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNicNoDefaultSendIf) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -238,7 +232,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNic) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -326,7 +320,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -346,7 +340,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -437,7 +431,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -457,7 +451,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -548,7 +542,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -568,7 +562,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -657,7 +651,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -677,7 +671,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -770,7 +764,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
// Set the default send interface.
ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
sizeof(iface)),
SyscallSucceeds());
@@ -794,7 +788,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -819,20 +813,6 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
-// Check that dropping a group membership that does not exist fails.
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastInvalidDrop) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- // Unregister from a membership that we didn't have.
- ip_mreq group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_DROP_MEMBERSHIP, &group,
- sizeof(group)),
- SyscallFailsWithErrno(EADDRNOTAVAIL));
-}
-
// Check that dropping a group membership prevents multicast packets from being
// delivered. Default send address configured by bind and group membership
// interface configured by address.
@@ -917,7 +897,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) {
// Register and unregister to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -943,260 +923,6 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) {
PosixErrorIs(EAGAIN, ::testing::_));
}
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfZero) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn iface = {};
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallSucceeds());
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfInvalidNic) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn iface = {};
- iface.imr_ifindex = -1;
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallFailsWithErrno(EADDRNOTAVAIL));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfInvalidAddr) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreq iface = {};
- iface.imr_interface.s_addr = inet_addr("255.255.255");
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface,
- sizeof(iface)),
- SyscallFailsWithErrno(EADDRNOTAVAIL));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetShort) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- // Create a valid full-sized request.
- ip_mreqn iface = {};
- iface.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
-
- // Send an optlen of 1 to check that optlen is enforced.
- EXPECT_THAT(
- setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &iface, 1),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfDefault) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfDefaultReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
- // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
- // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
- EXPECT_EQ(size, sizeof(in_addr));
-
- // getsockopt(IP_MULTICAST_IF) will only return the interface address which
- // hasn't been set.
- EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetAddrGetReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- in_addr set = {};
- set.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
- // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
- // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
- EXPECT_EQ(size, sizeof(in_addr));
- EXPECT_EQ(get.imr_multiaddr.s_addr, set.s_addr);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetReqAddrGetReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreq set = {};
- set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- // getsockopt(IP_MULTICAST_IF) can only return an in_addr, so it treats the
- // first sizeof(struct in_addr) bytes of struct ip_mreqn as a struct in_addr.
- // Conveniently, this corresponds to the field ip_mreqn::imr_multiaddr.
- EXPECT_EQ(size, sizeof(in_addr));
- EXPECT_EQ(get.imr_multiaddr.s_addr, set.imr_interface.s_addr);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetNicGetReqn) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn set = {};
- set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- ip_mreqn get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
- EXPECT_EQ(size, sizeof(in_addr));
- EXPECT_EQ(get.imr_multiaddr.s_addr, 0);
- EXPECT_EQ(get.imr_address.s_addr, 0);
- EXPECT_EQ(get.imr_ifindex, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetAddr) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- in_addr set = {};
- set.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, set.s_addr);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetReqAddr) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreq set = {};
- set.imr_interface.s_addr = htonl(INADDR_LOOPBACK);
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
-
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, set.imr_interface.s_addr);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfSetNic) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn set = {};
- set.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
- ASSERT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &set,
- sizeof(set)),
- SyscallSucceeds());
-
- in_addr get = {};
- socklen_t size = sizeof(get);
- ASSERT_THAT(
- getsockopt(socket1->get(), IPPROTO_IP, IP_MULTICAST_IF, &get, &size),
- SyscallSucceeds());
- EXPECT_EQ(size, sizeof(get));
- EXPECT_EQ(get.s_addr, 0);
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, TestJoinGroupNoIf) {
- // TODO(b/185517803): Fix for native test.
- SKIP_IF(!IsRunningOnGvisor());
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
- sizeof(group)),
- SyscallFailsWithErrno(ENODEV));
-}
-
-TEST_P(IPv4UDPUnboundSocketTest, TestJoinGroupInvalidIf) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
-
- ip_mreqn group = {};
- group.imr_address.s_addr = inet_addr("255.255.255");
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
- sizeof(group)),
- SyscallFailsWithErrno(ENODEV));
-}
-
-// Check that multiple memberships are not allowed on the same socket.
-TEST_P(IPv4UDPUnboundSocketTest, TestMultipleJoinsOnSingleSocket) {
- auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto socket2 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- auto fd = socket1->get();
- ip_mreqn group = {};
- group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
-
- EXPECT_THAT(
- setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
- SyscallSucceeds());
-
- EXPECT_THAT(
- setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)),
- SyscallFailsWithErrno(EADDRINUSE));
-}
-
// Check that two sockets can join the same multicast group at the same time.
TEST_P(IPv4UDPUnboundSocketTest, TestTwoSocketsJoinSameMulticastGroup) {
auto socket1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
@@ -1204,7 +930,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestTwoSocketsJoinSameMulticastGroup) {
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
EXPECT_THAT(setsockopt(socket1->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -1419,7 +1145,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenJoinThenReceive) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(socket2->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP, &group,
sizeof(group)),
SyscallSucceeds());
@@ -2110,16 +1836,11 @@ TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) {
EXPECT_EQ(cmsg->cmsg_level, level);
EXPECT_EQ(cmsg->cmsg_type, type);
- // Get loopback index.
- ifreq ifr = {};
- absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo");
- ASSERT_THAT(ioctl(sender->get(), SIOCGIFINDEX, &ifr), SyscallSucceeds());
- ASSERT_NE(ifr.ifr_ifindex, 0);
-
// Check the data
in_pktinfo received_pktinfo = {};
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo));
- EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex);
+ EXPECT_EQ(received_pktinfo.ipi_ifindex,
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
EXPECT_EQ(received_pktinfo.ipi_spec_dst.s_addr, htonl(INADDR_LOOPBACK));
EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK));
}
@@ -2439,7 +2160,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIPPacketInfo) {
// Register to receive multicast packets.
ip_mreqn group = {};
group.imr_multiaddr.s_addr = inet_addr(kMulticastAddress);
- group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"));
+ group.imr_ifindex = ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex());
ASSERT_THAT(setsockopt(receiver_socket->get(), IPPROTO_IP, IP_ADD_MEMBERSHIP,
&group, sizeof(group)),
SyscallSucceeds());
@@ -2484,16 +2205,10 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIPPacketInfo) {
EXPECT_EQ(cmsg->cmsg_level, IPPROTO_IP);
EXPECT_EQ(cmsg->cmsg_type, IP_PKTINFO);
- // Get loopback index.
- ifreq ifr = {};
- absl::SNPrintF(ifr.ifr_name, IFNAMSIZ, "lo");
- ASSERT_THAT(ioctl(receiver_socket->get(), SIOCGIFINDEX, &ifr),
- SyscallSucceeds());
- ASSERT_NE(ifr.ifr_ifindex, 0);
-
in_pktinfo received_pktinfo = {};
memcpy(&received_pktinfo, CMSG_DATA(cmsg), sizeof(in_pktinfo));
- EXPECT_EQ(received_pktinfo.ipi_ifindex, ifr.ifr_ifindex);
+ EXPECT_EQ(received_pktinfo.ipi_ifindex,
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex()));
if (IsRunningOnGvisor()) {
// This should actually be a unicast address assigned to the interface.
//
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.h b/test/syscalls/linux/socket_ipv4_udp_unbound.h
index f64c57645..3818a3490 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.h
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc
index f6e64c157..ebf2185f2 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_external_networking_test.cc
@@ -17,7 +17,7 @@
#include <vector>
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc
index f121c044d..bef284530 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback.cc
@@ -12,12 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <vector>
-
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ipv4_udp_unbound.h"
-#include "test/syscalls/linux/socket_test_util.h"
-#include "test/util/test_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc
index 8052bf404..f90a48630 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_netlink.cc
@@ -16,7 +16,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc
index 7ca6d52e4..5a7bca658 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_loopback_nogotsan.cc
@@ -18,7 +18,7 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -31,7 +31,7 @@ using IPv4UDPUnboundSocketNogotsanTest = SimpleSocketTest;
// We disable S/R because this test creates a large number of sockets.
TEST_P(IPv4UDPUnboundSocketNogotsanTest, UDPConnectPortExhaustion) {
auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- constexpr int kClients = 65536;
+ const int kClients = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
// Bind the first socket to the loopback and take note of the selected port.
auto addr = V4Loopback();
ASSERT_THAT(bind(receiver1->get(), AsSockAddr(&addr.addr), addr.addr_len),
@@ -61,7 +61,7 @@ TEST_P(IPv4UDPUnboundSocketNogotsanTest, UDPConnectPortExhaustion) {
// We disable S/R because this test creates a large number of sockets.
TEST_P(IPv4UDPUnboundSocketNogotsanTest, UDPBindPortExhaustion) {
auto receiver1 = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
- constexpr int kClients = 65536;
+ const int kClients = ASSERT_NO_ERRNO_AND_VALUE(MaybeLimitEphemeralPorts());
auto addr = V4Loopback();
// Disable cooperative S/R as we are making too many syscalls.
DisableSave ds;
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h
index 73e7836d5..17c8e2b84 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_NETLINK_UTIL_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV4_UDP_UNBOUND_NETLINK_UTIL_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.cc b/test/syscalls/linux/socket_ipv6_udp_unbound.cc
index a4e3371f4..612fd531c 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound.cc
@@ -31,9 +31,9 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/posix_error.h"
#include "test/util/save_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.h b/test/syscalls/linux/socket_ipv6_udp_unbound.h
index 71e160f73..060343eaf 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound.h
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc
index 09f070797..d72b6b53f 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking.cc
@@ -39,7 +39,7 @@ TEST_P(IPv6UDPUnboundExternalNetworkingSocketTest, TestJoinLeaveMulticast) {
.ipv6mr_multiaddr =
reinterpret_cast<sockaddr_in6*>(&multicast_addr.addr)->sin6_addr,
.ipv6mr_interface = static_cast<decltype(ipv6_mreq::ipv6mr_interface)>(
- ASSERT_NO_ERRNO_AND_VALUE(InterfaceIndex("lo"))),
+ ASSERT_NO_ERRNO_AND_VALUE(GetLoopbackIndex())),
};
ASSERT_THAT(setsockopt(receiver->get(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
&group_req, sizeof(group_req)),
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc
index 5c764b8fd..ff12eafc3 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_external_networking_test.cc
@@ -17,7 +17,7 @@
#include <vector>
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc
index 058336ecc..f11f444a2 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc
@@ -16,7 +16,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ipv6_udp_unbound.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc
index 17021ff82..565f9bc2e 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback_netlink.cc
@@ -16,7 +16,7 @@
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h b/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h
index 88098be82..f017a4c89 100644
--- a/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_netlink.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_NETLINK_UTIL_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_NETLINK_UTIL_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_netdevice.cc b/test/syscalls/linux/socket_netdevice.cc
index 5f8d7f981..c95c5305d 100644
--- a/test/syscalls/linux/socket_netdevice.cc
+++ b/test/syscalls/linux/socket_netdevice.cc
@@ -22,8 +22,8 @@
#include "gtest/gtest.h"
#include "absl/base/internal/endian.h"
#include "test/syscalls/linux/socket_netlink_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// Tests for netdevice queries.
@@ -37,6 +37,7 @@ using ::testing::AnyOf;
using ::testing::Eq;
TEST(NetdeviceTest, Loopback) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -60,6 +61,7 @@ TEST(NetdeviceTest, Loopback) {
}
TEST(NetdeviceTest, Netmask) {
+ SKIP_IF(IsRunningWithHostinet());
// We need an interface index to identify the loopback device.
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -135,6 +137,7 @@ TEST(NetdeviceTest, Netmask) {
}
TEST(NetdeviceTest, InterfaceName) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -168,6 +171,7 @@ TEST(NetdeviceTest, InterfaceFlags) {
}
TEST(NetdeviceTest, InterfaceMTU) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
@@ -181,6 +185,7 @@ TEST(NetdeviceTest, InterfaceMTU) {
}
TEST(NetdeviceTest, EthtoolGetTSInfo) {
+ SKIP_IF(IsRunningWithHostinet());
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
diff --git a/test/syscalls/linux/socket_netlink.cc b/test/syscalls/linux/socket_netlink.cc
index 4ec0fd4fa..c78529a14 100644
--- a/test/syscalls/linux/socket_netlink.cc
+++ b/test/syscalls/linux/socket_netlink.cc
@@ -18,8 +18,8 @@
#include <unistd.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// Tests for all netlink socket protocols.
diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc
index ee3c08770..d5e1ce0cc 100644
--- a/test/syscalls/linux/socket_netlink_route.cc
+++ b/test/syscalls/linux/socket_netlink_route.cc
@@ -29,10 +29,10 @@
#include "absl/strings/str_format.h"
#include "test/syscalls/linux/socket_netlink_route_util.h"
#include "test/syscalls/linux/socket_netlink_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// Tests for NETLINK_ROUTE sockets.
@@ -44,6 +44,7 @@ namespace {
constexpr uint32_t kSeq = 12345;
+using ::testing::_;
using ::testing::AnyOf;
using ::testing::Eq;
@@ -244,7 +245,7 @@ TEST(NetlinkRouteTest, GetLinkByIndexNotFound) {
req.ifm.ifi_index = 1234590;
EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
- PosixErrorIs(ENODEV, ::testing::_));
+ PosixErrorIs(ENODEV, _));
}
TEST(NetlinkRouteTest, GetLinkByNameNotFound) {
@@ -273,7 +274,112 @@ TEST(NetlinkRouteTest, GetLinkByNameNotFound) {
NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
- PosixErrorIs(ENODEV, ::testing::_));
+ PosixErrorIs(ENODEV, _));
+}
+
+TEST(NetlinkRouteTest, RemoveLoopbackByName) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+ Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
+
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
+
+ struct request {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifm;
+ struct rtattr rtattr;
+ char ifname[IFNAMSIZ];
+ char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
+ };
+
+ struct request req = {};
+ req.hdr.nlmsg_type = RTM_DELLINK;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = kSeq;
+ req.ifm.ifi_family = AF_UNSPEC;
+ req.rtattr.rta_type = IFLA_IFNAME;
+ req.rtattr.rta_len = RTA_LENGTH(loopback_link.name.size() + 1);
+ strncpy(req.ifname, loopback_link.name.c_str(), sizeof(req.ifname));
+ req.hdr.nlmsg_len =
+ NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
+
+ EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
+ PosixErrorIs(ENOTSUP, _));
+}
+
+TEST(NetlinkRouteTest, RemoveLoopbackByIndex) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+ Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
+
+ struct request {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifm;
+ };
+
+ struct request req = {};
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_type = RTM_DELLINK;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = kSeq;
+ req.ifm.ifi_family = AF_UNSPEC;
+ req.ifm.ifi_index = loopback_link.index;
+
+ EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
+ PosixErrorIs(ENOTSUP, _));
+}
+
+TEST(NetlinkRouteTest, RemoveLinkByIndexNotFound) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
+
+ struct request {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifm;
+ };
+
+ struct request req = {};
+ req.hdr.nlmsg_len = sizeof(req);
+ req.hdr.nlmsg_type = RTM_GETLINK;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = kSeq;
+ req.ifm.ifi_family = AF_UNSPEC;
+ req.ifm.ifi_index = 1234590;
+
+ EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
+ PosixErrorIs(ENODEV, _));
+}
+
+TEST(NetlinkRouteTest, RemoveLinkByNameNotFound) {
+ const std::string name = "nodevice?!";
+
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+ FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
+
+ struct request {
+ struct nlmsghdr hdr;
+ struct ifinfomsg ifm;
+ struct rtattr rtattr;
+ char ifname[IFNAMSIZ];
+ char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
+ };
+
+ struct request req = {};
+ req.hdr.nlmsg_type = RTM_DELLINK;
+ req.hdr.nlmsg_flags = NLM_F_REQUEST;
+ req.hdr.nlmsg_seq = kSeq;
+ req.ifm.ifi_family = AF_UNSPEC;
+ req.rtattr.rta_type = IFLA_IFNAME;
+ req.rtattr.rta_len = RTA_LENGTH(name.size() + 1);
+ strncpy(req.ifname, name.c_str(), sizeof(req.ifname));
+ req.hdr.nlmsg_len =
+ NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
+
+ EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
+ PosixErrorIs(ENODEV, _));
}
TEST(NetlinkRouteTest, MsgHdrMsgUnsuppType) {
@@ -295,7 +401,7 @@ TEST(NetlinkRouteTest, MsgHdrMsgUnsuppType) {
req.ifm.ifi_family = AF_UNSPEC;
EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
- PosixErrorIs(EOPNOTSUPP, ::testing::_));
+ PosixErrorIs(EOPNOTSUPP, _));
}
TEST(NetlinkRouteTest, MsgHdrMsgTrunc) {
@@ -536,7 +642,7 @@ TEST(NetlinkRouteTest, AddAndRemoveAddr) {
// Second delete should fail, as address no longer exists.
EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET,
/*prefixlen=*/24, &addr, sizeof(addr)),
- PosixErrorIs(EADDRNOTAVAIL, ::testing::_));
+ PosixErrorIs(EADDRNOTAVAIL, _));
});
// Replace an existing address should succeed.
@@ -546,7 +652,7 @@ TEST(NetlinkRouteTest, AddAndRemoveAddr) {
// Create exclusive should fail, as we created the address above.
EXPECT_THAT(LinkAddExclusiveLocalAddr(loopback_link.index, AF_INET,
/*prefixlen=*/24, &addr, sizeof(addr)),
- PosixErrorIs(EEXIST, ::testing::_));
+ PosixErrorIs(EEXIST, _));
}
// GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request.
diff --git a/test/syscalls/linux/socket_netlink_uevent.cc b/test/syscalls/linux/socket_netlink_uevent.cc
index da425bed4..9e025911b 100644
--- a/test/syscalls/linux/socket_netlink_uevent.cc
+++ b/test/syscalls/linux/socket_netlink_uevent.cc
@@ -20,8 +20,8 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_netlink_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
// Tests for NETLINK_KOBJECT_UEVENT sockets.
diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc
index bdebea321..c1bff3c65 100644
--- a/test/syscalls/linux/socket_netlink_util.cc
+++ b/test/syscalls/linux/socket_netlink_util.cc
@@ -22,7 +22,7 @@
#include <vector>
#include "absl/strings/str_cat.h"
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_non_blocking.cc b/test/syscalls/linux/socket_non_blocking.cc
index c3520cadd..3d09485d3 100644
--- a/test/syscalls/linux/socket_non_blocking.cc
+++ b/test/syscalls/linux/socket_non_blocking.cc
@@ -20,8 +20,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_non_blocking.h b/test/syscalls/linux/socket_non_blocking.h
index bd3e02fd2..604206cfb 100644
--- a/test/syscalls/linux/socket_non_blocking.h
+++ b/test/syscalls/linux/socket_non_blocking.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_BLOCKING_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_non_stream.cc b/test/syscalls/linux/socket_non_stream.cc
index c61817f14..7c3310909 100644
--- a/test/syscalls/linux/socket_non_stream.cc
+++ b/test/syscalls/linux/socket_non_stream.cc
@@ -20,8 +20,8 @@
#include "gtest/gtest.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_non_stream.h b/test/syscalls/linux/socket_non_stream.h
index 469fbe6a2..4876730f9 100644
--- a/test/syscalls/linux/socket_non_stream.h
+++ b/test/syscalls/linux/socket_non_stream.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_non_stream_blocking.cc b/test/syscalls/linux/socket_non_stream_blocking.cc
index b052f6e61..ac33407f8 100644
--- a/test/syscalls/linux/socket_non_stream_blocking.cc
+++ b/test/syscalls/linux/socket_non_stream_blocking.cc
@@ -22,8 +22,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/socket_non_stream_blocking.h b/test/syscalls/linux/socket_non_stream_blocking.h
index 6e205a039..71520bb37 100644
--- a/test/syscalls/linux/socket_non_stream_blocking.h
+++ b/test/syscalls/linux/socket_non_stream_blocking.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_NON_STREAM_BLOCKING_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_stream.cc b/test/syscalls/linux/socket_stream.cc
index 6522b2e01..11903f28b 100644
--- a/test/syscalls/linux/socket_stream.cc
+++ b/test/syscalls/linux/socket_stream.cc
@@ -21,8 +21,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_stream.h b/test/syscalls/linux/socket_stream.h
index b837b8f8c..dc6fb2f98 100644
--- a/test/syscalls/linux/socket_stream.h
+++ b/test/syscalls/linux/socket_stream.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_stream_blocking.cc b/test/syscalls/linux/socket_stream_blocking.cc
index 0743322ac..e168f79ff 100644
--- a/test/syscalls/linux/socket_stream_blocking.cc
+++ b/test/syscalls/linux/socket_stream_blocking.cc
@@ -22,8 +22,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
#include "test/util/timer_util.h"
diff --git a/test/syscalls/linux/socket_stream_blocking.h b/test/syscalls/linux/socket_stream_blocking.h
index 9fd19ff90..f760188f6 100644
--- a/test/syscalls/linux/socket_stream_blocking.h
+++ b/test/syscalls/linux/socket_stream_blocking.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_BLOCKING_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_stream_nonblock.cc b/test/syscalls/linux/socket_stream_nonblock.cc
index 74d608741..788fae906 100644
--- a/test/syscalls/linux/socket_stream_nonblock.cc
+++ b/test/syscalls/linux/socket_stream_nonblock.cc
@@ -20,8 +20,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_stream_nonblock.h b/test/syscalls/linux/socket_stream_nonblock.h
index c3b7fad91..d1adaa95c 100644
--- a/test/syscalls/linux/socket_stream_nonblock.h
+++ b/test/syscalls/linux/socket_stream_nonblock.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_STREAM_NONBLOCK_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_unix.cc b/test/syscalls/linux/socket_unix.cc
index 591cab3fd..43433eaae 100644
--- a/test/syscalls/linux/socket_unix.cc
+++ b/test/syscalls/linux/socket_unix.cc
@@ -26,8 +26,11 @@
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/file_descriptor.h"
+#include "test/util/memory_util.h"
+#include "test/util/socket_util.h"
+#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -268,6 +271,18 @@ TEST_P(UnixSocketPairTest, SocketReopenFromProcfs) {
}
}
+// Repro for b/196804997.
+TEST_P(UnixSocketPairTest, SendFromMmapBeyondEof) {
+ TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY));
+ Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
+ Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, fd.get(), 0));
+
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ ASSERT_THAT(send(sockets->first_fd(), m.ptr(), m.len(), 0),
+ SyscallFailsWithErrno(EFAULT));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/socket_unix.h b/test/syscalls/linux/socket_unix.h
index 3625cc404..f6405f399 100644
--- a/test/syscalls/linux/socket_unix.h
+++ b/test/syscalls/linux/socket_unix.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_unix_abstract_nonblock.cc b/test/syscalls/linux/socket_unix_abstract_nonblock.cc
index 8bef76b67..617c5bfe5 100644
--- a/test/syscalls/linux/socket_unix_abstract_nonblock.cc
+++ b/test/syscalls/linux/socket_unix_abstract_nonblock.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_non_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_blocking_local.cc b/test/syscalls/linux/socket_unix_blocking_local.cc
index 77cb8c6d6..ba34320bc 100644
--- a/test/syscalls/linux/socket_unix_blocking_local.cc
+++ b/test/syscalls/linux/socket_unix_blocking_local.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_cmsg.cc b/test/syscalls/linux/socket_unix_cmsg.cc
index 22a4ee0d1..6191b1448 100644
--- a/test/syscalls/linux/socket_unix_cmsg.cc
+++ b/test/syscalls/linux/socket_unix_cmsg.cc
@@ -26,8 +26,8 @@
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
diff --git a/test/syscalls/linux/socket_unix_cmsg.h b/test/syscalls/linux/socket_unix_cmsg.h
index 431606903..f5a276155 100644
--- a/test/syscalls/linux/socket_unix_cmsg.h
+++ b/test/syscalls/linux/socket_unix_cmsg.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_CMSG_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_unix_dgram.cc b/test/syscalls/linux/socket_unix_dgram.cc
index 5b0844493..d43c83d71 100644
--- a/test/syscalls/linux/socket_unix_dgram.cc
+++ b/test/syscalls/linux/socket_unix_dgram.cc
@@ -20,8 +20,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_dgram.h b/test/syscalls/linux/socket_unix_dgram.h
index 0764ef85b..e9b8373a5 100644
--- a/test/syscalls/linux/socket_unix_dgram.h
+++ b/test/syscalls/linux/socket_unix_dgram.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_DGRAM_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_unix_dgram_local.cc b/test/syscalls/linux/socket_unix_dgram_local.cc
index 31d2d5216..4760630b9 100644
--- a/test/syscalls/linux/socket_unix_dgram_local.cc
+++ b/test/syscalls/linux/socket_unix_dgram_local.cc
@@ -15,10 +15,10 @@
#include <vector>
#include "test/syscalls/linux/socket_non_stream.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix_dgram.h"
#include "test/syscalls/linux/socket_unix_non_stream.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc
index 2db8b68d3..ca277122e 100644
--- a/test/syscalls/linux/socket_unix_dgram_non_blocking.cc
+++ b/test/syscalls/linux/socket_unix_dgram_non_blocking.cc
@@ -16,8 +16,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_domain.cc b/test/syscalls/linux/socket_unix_domain.cc
index f7dff8b4d..d8cb5b892 100644
--- a/test/syscalls/linux/socket_unix_domain.cc
+++ b/test/syscalls/linux/socket_unix_domain.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_generic.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc
index 6700b4d90..cdd38f681 100644
--- a/test/syscalls/linux/socket_unix_filesystem_nonblock.cc
+++ b/test/syscalls/linux/socket_unix_filesystem_nonblock.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_non_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_non_stream.cc b/test/syscalls/linux/socket_unix_non_stream.cc
index 9425e87a6..d18f9e7b0 100644
--- a/test/syscalls/linux/socket_unix_non_stream.cc
+++ b/test/syscalls/linux/socket_unix_non_stream.cc
@@ -19,9 +19,9 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/memory_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_non_stream.h b/test/syscalls/linux/socket_unix_non_stream.h
index 7478ab172..44d1c0033 100644
--- a/test/syscalls/linux/socket_unix_non_stream.h
+++ b/test/syscalls/linux/socket_unix_non_stream.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_NON_STREAM_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
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 fddcdf1c5..92f40f5e5 100644
--- a/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc
+++ b/test/syscalls/linux/socket_unix_non_stream_blocking_local.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_non_stream_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_pair.cc b/test/syscalls/linux/socket_unix_pair.cc
index 85999db04..28a437339 100644
--- a/test/syscalls/linux/socket_unix_pair.cc
+++ b/test/syscalls/linux/socket_unix_pair.cc
@@ -14,10 +14,10 @@
#include <vector>
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix.h"
#include "test/syscalls/linux/socket_unix_cmsg.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_pair_nonblock.cc b/test/syscalls/linux/socket_unix_pair_nonblock.cc
index 281410a9a..39360896b 100644
--- a/test/syscalls/linux/socket_unix_pair_nonblock.cc
+++ b/test/syscalls/linux/socket_unix_pair_nonblock.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_non_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_seqpacket.cc b/test/syscalls/linux/socket_unix_seqpacket.cc
index d6e7031c0..2a2741eb9 100644
--- a/test/syscalls/linux/socket_unix_seqpacket.cc
+++ b/test/syscalls/linux/socket_unix_seqpacket.cc
@@ -20,8 +20,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_seqpacket.h b/test/syscalls/linux/socket_unix_seqpacket.h
index 30d9b9edf..5bb36af92 100644
--- a/test/syscalls/linux/socket_unix_seqpacket.h
+++ b/test/syscalls/linux/socket_unix_seqpacket.h
@@ -15,7 +15,7 @@
#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_
#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_UNIX_SEQPACKET_H_
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/socket_unix_seqpacket_local.cc b/test/syscalls/linux/socket_unix_seqpacket_local.cc
index 69a5f150d..492416a77 100644
--- a/test/syscalls/linux/socket_unix_seqpacket_local.cc
+++ b/test/syscalls/linux/socket_unix_seqpacket_local.cc
@@ -15,10 +15,10 @@
#include <vector>
#include "test/syscalls/linux/socket_non_stream.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/socket_unix_non_stream.h"
#include "test/syscalls/linux/socket_unix_seqpacket.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc
index 3ff810914..2f3cfc3f3 100644
--- a/test/syscalls/linux/socket_unix_stream.cc
+++ b/test/syscalls/linux/socket_unix_stream.cc
@@ -19,8 +19,8 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -181,6 +181,21 @@ TEST_P(StreamUnixSocketPairTest, SetSocketSendBuf) {
ASSERT_EQ(quarter_sz, val);
}
+TEST_P(StreamUnixSocketPairTest, SendBufferOverflow) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ auto s = sockets->first_fd();
+
+ constexpr int kBufSz = 4096;
+ std::vector<char> buf(kBufSz * 4);
+ ASSERT_THAT(RetryEINTR(send)(s, buf.data(), buf.size(), MSG_DONTWAIT),
+ SyscallSucceeds());
+ // The new buffer size should be smaller that the amount of data in the queue.
+ ASSERT_THAT(setsockopt(s, SOL_SOCKET, SO_SNDBUF, &kBufSz, sizeof(kBufSz)),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(send)(s, buf.data(), buf.size(), MSG_DONTWAIT),
+ SyscallFailsWithErrno(EAGAIN));
+}
+
TEST_P(StreamUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int sock = sockets->first_fd();
diff --git a/test/syscalls/linux/socket_unix_stream_blocking_local.cc b/test/syscalls/linux/socket_unix_stream_blocking_local.cc
index 8429bd429..97a6bb327 100644
--- a/test/syscalls/linux/socket_unix_stream_blocking_local.cc
+++ b/test/syscalls/linux/socket_unix_stream_blocking_local.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_stream_blocking.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_stream_local.cc b/test/syscalls/linux/socket_unix_stream_local.cc
index a7e3449a9..4b267ccae 100644
--- a/test/syscalls/linux/socket_unix_stream_local.cc
+++ b/test/syscalls/linux/socket_unix_stream_local.cc
@@ -15,8 +15,8 @@
#include <vector>
#include "test/syscalls/linux/socket_stream.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
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 4b763c8e2..d7bf7747b 100644
--- a/test/syscalls/linux/socket_unix_stream_nonblock_local.cc
+++ b/test/syscalls/linux/socket_unix_stream_nonblock_local.cc
@@ -14,8 +14,8 @@
#include <vector>
#include "test/syscalls/linux/socket_stream_nonblock.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_unbound_abstract.cc b/test/syscalls/linux/socket_unix_unbound_abstract.cc
index dd3d25450..0f6864266 100644
--- a/test/syscalls/linux/socket_unix_unbound_abstract.cc
+++ b/test/syscalls/linux/socket_unix_unbound_abstract.cc
@@ -16,8 +16,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_unbound_dgram.cc b/test/syscalls/linux/socket_unix_unbound_dgram.cc
index 907dca0f1..ccf2c94a1 100644
--- a/test/syscalls/linux/socket_unix_unbound_dgram.cc
+++ b/test/syscalls/linux/socket_unix_unbound_dgram.cc
@@ -17,8 +17,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_unbound_filesystem.cc b/test/syscalls/linux/socket_unix_unbound_filesystem.cc
index a035fb095..811fe12a1 100644
--- a/test/syscalls/linux/socket_unix_unbound_filesystem.cc
+++ b/test/syscalls/linux/socket_unix_unbound_filesystem.cc
@@ -17,9 +17,9 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc
index cb99030f5..e22018890 100644
--- a/test/syscalls/linux/socket_unix_unbound_seqpacket.cc
+++ b/test/syscalls/linux/socket_unix_unbound_seqpacket.cc
@@ -16,8 +16,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/socket_unix_unbound_stream.cc b/test/syscalls/linux/socket_unix_unbound_stream.cc
index f185dded3..b10062bc2 100644
--- a/test/syscalls/linux/socket_unix_unbound_stream.cc
+++ b/test/syscalls/linux/socket_unix_unbound_stream.cc
@@ -16,8 +16,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc
index c85f6da0b..4a10ae8d2 100644
--- a/test/syscalls/linux/splice.cc
+++ b/test/syscalls/linux/splice.cc
@@ -195,81 +195,6 @@ TEST(SpliceTest, PipeOffsets) {
SyscallFailsWithErrno(ESPIPE));
}
-// Event FDs may be used with splice without an offset.
-TEST(SpliceTest, FromEventFD) {
- // Open the input eventfd with an initial value so that it is readable.
- constexpr uint64_t kEventFDValue = 1;
- int efd;
- ASSERT_THAT(efd = eventfd(kEventFDValue, 0), SyscallSucceeds());
- const FileDescriptor in_fd(efd);
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Splice 8-byte eventfd value to pipe.
- constexpr int kEventFDSize = 8;
- EXPECT_THAT(splice(in_fd.get(), nullptr, wfd.get(), nullptr, kEventFDSize, 0),
- SyscallSucceedsWithValue(kEventFDSize));
-
- // Contents should be equal.
- std::vector<char> rbuf(kEventFDSize);
- ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()),
- SyscallSucceedsWithValue(kEventFDSize));
- EXPECT_EQ(memcmp(rbuf.data(), &kEventFDValue, rbuf.size()), 0);
-}
-
-// Event FDs may not be used with splice with an offset.
-TEST(SpliceTest, FromEventFDOffset) {
- int efd;
- ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds());
- const FileDescriptor in_fd(efd);
-
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Attempt to splice 8-byte eventfd value to pipe with offset.
- //
- // This is not allowed because eventfd doesn't support pread.
- constexpr int kEventFDSize = 8;
- loff_t in_off = 0;
- EXPECT_THAT(splice(in_fd.get(), &in_off, wfd.get(), nullptr, kEventFDSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
-// Event FDs may not be used with splice with an offset.
-TEST(SpliceTest, ToEventFDOffset) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- const FileDescriptor wfd(fds[1]);
-
- // Fill with a value.
- constexpr int kEventFDSize = 8;
- std::vector<char> buf(kEventFDSize);
- buf[0] = 1;
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kEventFDSize));
-
- int efd;
- ASSERT_THAT(efd = eventfd(0, 0), SyscallSucceeds());
- const FileDescriptor out_fd(efd);
-
- // Attempt to splice 8-byte eventfd value to pipe with offset.
- //
- // This is not allowed because eventfd doesn't support pwrite.
- loff_t out_off = 0;
- EXPECT_THAT(
- splice(rfd.get(), nullptr, out_fd.get(), &out_off, kEventFDSize, 0),
- SyscallFailsWithErrno(EINVAL));
-}
-
TEST(SpliceTest, ToPipe) {
// Open the input file.
const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
@@ -852,34 +777,6 @@ TEST(SpliceTest, FromPipeMaxFileSize) {
EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0);
}
-TEST(SpliceTest, FromPipeToDevZero) {
- // Create a new pipe.
- int fds[2];
- ASSERT_THAT(pipe(fds), SyscallSucceeds());
- const FileDescriptor rfd(fds[0]);
- FileDescriptor wfd(fds[1]);
-
- // Fill with some random data.
- std::vector<char> buf(kPageSize);
- RandomizeBuffer(buf.data(), buf.size());
- ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()),
- SyscallSucceedsWithValue(kPageSize));
-
- const FileDescriptor zero =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_WRONLY));
-
- // Close the write end to prevent blocking below.
- wfd.reset();
-
- // Splice to /dev/zero. The first call should empty the pipe, and the return
- // value should not exceed the number of bytes available for reading.
- EXPECT_THAT(
- splice(rfd.get(), nullptr, zero.get(), nullptr, kPageSize + 123, 0),
- SyscallSucceedsWithValue(kPageSize));
- EXPECT_THAT(splice(rfd.get(), nullptr, zero.get(), nullptr, 1, 0),
- SyscallSucceedsWithValue(0));
-}
-
static volatile int signaled = 0;
void SigUsr1Handler(int sig, siginfo_t* info, void* context) { signaled = 1; }
diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc
index 72f888659..19dc80d0c 100644
--- a/test/syscalls/linux/stat.cc
+++ b/test/syscalls/linux/stat.cc
@@ -765,7 +765,7 @@ TEST_F(StatTest, StatxSymlink) {
SKIP_IF(!IsRunningOnGvisor() && statx(-1, nullptr, 0, 0, nullptr) < 0 &&
errno == ENOSYS);
- std::string parent_dir = "/tmp";
+ std::string parent_dir = GetAbsoluteTestTmpdir();
TempPath link = ASSERT_NO_ERRNO_AND_VALUE(
TempPath::CreateSymlinkTo(parent_dir, test_file_name_));
std::string p = link.path();
diff --git a/test/syscalls/linux/statfs.cc b/test/syscalls/linux/statfs.cc
index d4ea8e026..d057cdc09 100644
--- a/test/syscalls/linux/statfs.cc
+++ b/test/syscalls/linux/statfs.cc
@@ -28,7 +28,7 @@ namespace testing {
namespace {
TEST(StatfsTest, CannotStatBadPath) {
- auto temp_file = NewTempAbsPathInDir("/tmp");
+ auto temp_file = NewTempAbsPath();
struct statfs st;
EXPECT_THAT(statfs(temp_file.c_str(), &st), SyscallFailsWithErrno(ENOENT));
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index 5bfdecc79..3fbbf1423 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -29,9 +29,9 @@
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -1182,6 +1182,62 @@ TEST_P(SimpleTcpSocketTest, SelfConnectSend) {
EXPECT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceedsWithValue(0));
}
+TEST_P(SimpleTcpSocketTest, SelfConnectSendShutdownWrite) {
+ // Initialize address to the loopback one.
+ sockaddr_storage addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
+ socklen_t addrlen = sizeof(addr);
+
+ const FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(s.get(), AsSockAddr(&addr), addrlen), SyscallSucceeds());
+ // Get the bound port.
+ ASSERT_THAT(getsockname(s.get(), AsSockAddr(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(s.get(), AsSockAddr(&addr), addrlen),
+ SyscallSucceeds());
+
+ // Write enough data to fill send and receive buffers.
+ size_t write_size = 24 << 20; // 24 MiB.
+ std::vector<char> writebuf(write_size);
+
+ ScopedThread t([&s]() {
+ absl::SleepFor(absl::Milliseconds(250));
+ ASSERT_THAT(shutdown(s.get(), SHUT_WR), SyscallSucceeds());
+ });
+
+ // Try to send the whole thing.
+ int n;
+ ASSERT_THAT(n = SendFd(s.get(), writebuf.data(), writebuf.size(), 0),
+ SyscallFailsWithErrno(EPIPE));
+}
+
+TEST_P(SimpleTcpSocketTest, SelfConnectRecvShutdownRead) {
+ // Initialize address to the loopback one.
+ sockaddr_storage addr =
+ ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
+ socklen_t addrlen = sizeof(addr);
+
+ const FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ ASSERT_THAT(bind(s.get(), AsSockAddr(&addr), addrlen), SyscallSucceeds());
+ // Get the bound port.
+ ASSERT_THAT(getsockname(s.get(), AsSockAddr(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(s.get(), AsSockAddr(&addr), addrlen),
+ SyscallSucceeds());
+
+ ScopedThread t([&s]() {
+ absl::SleepFor(absl::Milliseconds(250));
+ ASSERT_THAT(shutdown(s.get(), SHUT_RD), SyscallSucceeds());
+ });
+
+ char buf[1];
+ EXPECT_THAT(recv(s.get(), buf, 0, 0), SyscallSucceedsWithValue(0));
+}
+
void NonBlockingConnect(int family, int16_t pollMask) {
const FileDescriptor listener =
ASSERT_NO_ERRNO_AND_VALUE(Socket(family, SOCK_STREAM, IPPROTO_TCP));
@@ -2032,6 +2088,66 @@ TEST_P(SimpleTcpSocketTest, ConnectUnspecifiedAddress) {
}
}
+// Tests that send will return EWOULDBLOCK initially with large buffer and will
+// succeed after the send buffer size is increased.
+TEST_P(TcpSocketTest, SendUnblocksOnSendBufferIncrease) {
+ // Set the FD to O_NONBLOCK.
+ int opts;
+ ASSERT_THAT(opts = fcntl(first_fd, F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(first_fd, F_SETFL, opts), SyscallSucceeds());
+
+ // Get maximum buffer size by trying to set it to a large value.
+ constexpr int kSndBufSz = 0xffffffff;
+ ASSERT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &kSndBufSz,
+ sizeof(kSndBufSz)),
+ SyscallSucceeds());
+
+ int max_buffer_sz = 0;
+ socklen_t max_len = sizeof(max_buffer_sz);
+ ASSERT_THAT(
+ getsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &max_buffer_sz, &max_len),
+ SyscallSucceeds());
+
+ int buffer_sz = max_buffer_sz >> 2;
+ EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &buffer_sz,
+ sizeof(buffer_sz)),
+ SyscallSucceedsWithValue(0));
+
+ // Create a large buffer that will be used for sending.
+ std::vector<char> buffer(max_buffer_sz);
+
+ // Write until we receive an error.
+ while (RetryEINTR(send)(first_fd, buffer.data(), buffer.size(), 0) != -1) {
+ // Sleep to give linux a chance to move data from the send buffer to the
+ // receive buffer.
+ usleep(10000); // 10ms.
+ }
+
+ // The last error should have been EWOULDBLOCK.
+ ASSERT_EQ(errno, EWOULDBLOCK);
+
+ ScopedThread send_thread([this]() {
+ int flags = 0;
+ ASSERT_THAT(flags = fcntl(first_fd, F_GETFL), SyscallSucceeds());
+ EXPECT_THAT(fcntl(first_fd, F_SETFL, flags & ~O_NONBLOCK),
+ SyscallSucceeds());
+
+ // Expect the send() to succeed.
+ char buffer;
+ ASSERT_THAT(RetryEINTR(send)(first_fd, &buffer, sizeof(buffer), 0),
+ SyscallSucceeds());
+ });
+
+ // Set SO_SNDBUF to maximum buffer size allowed.
+ buffer_sz = max_buffer_sz >> 1;
+ EXPECT_THAT(setsockopt(first_fd, SOL_SOCKET, SO_SNDBUF, &buffer_sz,
+ sizeof(buffer_sz)),
+ SyscallSucceedsWithValue(0));
+
+ send_thread.Join();
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest,
::testing::Values(AF_INET, AF_INET6));
diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc
index 279fe342c..956208bee 100644
--- a/test/syscalls/linux/tuntap.cc
+++ b/test/syscalls/linux/tuntap.cc
@@ -24,16 +24,18 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <cstddef>
+
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/ascii.h"
#include "absl/strings/str_split.h"
#include "test/syscalls/linux/socket_netlink_route_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -44,6 +46,7 @@ constexpr int kIPLen = 4;
constexpr const char kDevNetTun[] = "/dev/net/tun";
constexpr const char kTapName[] = "tap0";
+constexpr const char kTunName[] = "tun0";
#define kTapIPAddr htonl(0x0a000001) /* Inet 10.0.0.1 */
#define kTapPeerIPAddr htonl(0x0a000002) /* Inet 10.0.0.2 */
@@ -70,29 +73,14 @@ PosixErrorOr<Link> GetLinkByName(const std::string& name) {
return PosixError(ENOENT, "interface not found");
}
-struct pihdr {
- uint16_t pi_flags;
- uint16_t pi_protocol;
-} __attribute__((packed));
-
-struct ping_pkt {
- pihdr pi;
- struct ethhdr eth;
- struct iphdr ip;
- struct icmphdr icmp;
+struct ping_ip_pkt {
+ iphdr ip;
+ icmphdr icmp;
char payload[64];
} __attribute__((packed));
-ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip,
- const uint8_t dstmac[ETH_ALEN],
- const in_addr_t dstip) {
- ping_pkt pkt = {};
-
- pkt.pi.pi_protocol = htons(ETH_P_IP);
-
- memcpy(pkt.eth.h_dest, dstmac, sizeof(pkt.eth.h_dest));
- memcpy(pkt.eth.h_source, srcmac, sizeof(pkt.eth.h_source));
- pkt.eth.h_proto = htons(ETH_P_IP);
+ping_ip_pkt CreatePingIPPacket(const in_addr_t srcip, const in_addr_t dstip) {
+ ping_ip_pkt pkt = {};
pkt.ip.ihl = 5;
pkt.ip.version = 4;
@@ -119,6 +107,33 @@ ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip,
return pkt;
}
+struct pihdr {
+ uint16_t pi_flags;
+ uint16_t pi_protocol;
+} __attribute__((packed));
+
+struct ping_pkt {
+ pihdr pi;
+ ethhdr eth;
+ ping_ip_pkt ip_pkt;
+} __attribute__((packed));
+
+ping_pkt CreatePingPacket(const uint8_t srcmac[ETH_ALEN], const in_addr_t srcip,
+ const uint8_t dstmac[ETH_ALEN],
+ const in_addr_t dstip) {
+ ping_pkt pkt = {};
+
+ pkt.pi.pi_protocol = htons(ETH_P_IP);
+
+ memcpy(pkt.eth.h_dest, dstmac, sizeof(pkt.eth.h_dest));
+ memcpy(pkt.eth.h_source, srcmac, sizeof(pkt.eth.h_source));
+ pkt.eth.h_proto = htons(ETH_P_IP);
+
+ pkt.ip_pkt = CreatePingIPPacket(srcip, dstip);
+
+ return pkt;
+}
+
struct arp_pkt {
pihdr pi;
struct ethhdr eth;
@@ -271,13 +286,26 @@ TEST_F(TuntapTest, WriteToDownDevice) {
EXPECT_THAT(write(fd.get(), buf, sizeof(buf)), SyscallFailsWithErrno(EIO));
}
-PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
- const in_addr_t dev_addr) {
+struct TunTapInterface {
+ FileDescriptor fd;
+ Link link;
+};
+
+PosixErrorOr<TunTapInterface> OpenAndAttachTunTap(const std::string& dev_name,
+ const in_addr_t dev_addr,
+ bool tap, bool no_pi) {
// Interface creation.
ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, Open(kDevNetTun, O_RDWR));
struct ifreq ifr_set = {};
- ifr_set.ifr_flags = IFF_TAP;
+ if (tap) {
+ ifr_set.ifr_flags |= IFF_TAP;
+ } else {
+ ifr_set.ifr_flags |= IFF_TUN;
+ }
+ if (no_pi) {
+ ifr_set.ifr_flags |= IFF_NO_PI;
+ }
strncpy(ifr_set.ifr_name, dev_name.c_str(), IFNAMSIZ);
if (ioctl(fd.get(), TUNSETIFF, &ifr_set) < 0) {
return PosixError(errno);
@@ -293,13 +321,15 @@ PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
if (!IsRunningOnGvisor()) {
// FIXME(b/110961832): gVisor doesn't support setting MAC address on
// interfaces yet.
- RETURN_IF_ERRNO(LinkSetMacAddr(link.index, kMacA, sizeof(kMacA)));
+ if (tap) {
+ RETURN_IF_ERRNO(LinkSetMacAddr(link.index, kMacA, sizeof(kMacA)));
+ }
// FIXME(b/110961832): gVisor always creates enabled/up'd interfaces.
RETURN_IF_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP));
}
- return fd;
+ return TunTapInterface{.fd = std::move(fd), .link = std::move(link)};
}
// This test sets up a TAP device and pings kernel by sending ICMP echo request.
@@ -319,8 +349,9 @@ PosixErrorOr<FileDescriptor> OpenAndAttachTap(const std::string& dev_name,
TEST_F(TuntapTest, PingKernel) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
+ const auto& [fd, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
+
ping_pkt ping_req =
CreatePingPacket(kMacB, kTapPeerIPAddr, kMacA, kTapIPAddr);
std::string arp_rep =
@@ -362,10 +393,10 @@ TEST_F(TuntapTest, PingKernel) {
// Process ping response packet.
if (n >= sizeof(ping_pkt) && r.pi.pi_protocol == ping_req.pi.pi_protocol &&
- r.ping.ip.protocol == ping_req.ip.protocol &&
- !memcmp(&r.ping.ip.saddr, &ping_req.ip.daddr, kIPLen) &&
- !memcmp(&r.ping.ip.daddr, &ping_req.ip.saddr, kIPLen) &&
- r.ping.icmp.type == 0 && r.ping.icmp.code == 0) {
+ r.ping.ip_pkt.ip.protocol == ping_req.ip_pkt.ip.protocol &&
+ !memcmp(&r.ping.ip_pkt.ip.saddr, &ping_req.ip_pkt.ip.daddr, kIPLen) &&
+ !memcmp(&r.ping.ip_pkt.ip.daddr, &ping_req.ip_pkt.ip.saddr, kIPLen) &&
+ r.ping.ip_pkt.icmp.type == 0 && r.ping.ip_pkt.icmp.code == 0) {
// Ends and passes the test.
break;
}
@@ -375,8 +406,8 @@ TEST_F(TuntapTest, PingKernel) {
TEST_F(TuntapTest, SendUdpTriggersArpResolution) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
+ const auto& [fd, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
// Send a UDP packet to remote.
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
@@ -413,6 +444,45 @@ TEST_F(TuntapTest, SendUdpTriggersArpResolution) {
}
}
+TEST_F(TuntapTest, TUNNoPacketInfo) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+
+ // Interface creation.
+ FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
+
+ struct ifreq ifr_set = {};
+ ifr_set.ifr_flags = IFF_TUN | IFF_NO_PI;
+ strncpy(ifr_set.ifr_name, kTunName, IFNAMSIZ);
+ EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr_set), SyscallSucceeds());
+
+ // Interface setup.
+ auto link = ASSERT_NO_ERRNO_AND_VALUE(GetLinkByName(kTunName));
+ const struct in_addr dev_ipv4_addr = {.s_addr = kTapIPAddr};
+ EXPECT_NO_ERRNO(LinkAddLocalAddr(link.index, AF_INET, 24, &dev_ipv4_addr,
+ sizeof(dev_ipv4_addr)));
+
+ ping_ip_pkt ping_req = CreatePingIPPacket(kTapPeerIPAddr, kTapIPAddr);
+
+ // Send ICMP query
+ EXPECT_THAT(write(fd.get(), &ping_req, sizeof(ping_req)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+
+ // Receive loop to process inbound packets.
+ while (1) {
+ ping_ip_pkt ping_resp = {};
+ EXPECT_THAT(read(fd.get(), &ping_resp, sizeof(ping_req)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+
+ // Process ping response packet.
+ if (!memcmp(&ping_resp.ip.saddr, &ping_req.ip.daddr, kIPLen) &&
+ !memcmp(&ping_resp.ip.daddr, &ping_req.ip.saddr, kIPLen) &&
+ ping_resp.icmp.type == 0 && ping_resp.icmp.code == 0) {
+ // Ends and passes the test.
+ break;
+ }
+ }
+}
+
// TCPBlockingConnectFailsArpResolution tests for TCP connect to fail on link
// address resolution failure to a routable, but non existent peer.
TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) {
@@ -421,8 +491,8 @@ TEST_F(TuntapTest, TCPBlockingConnectFailsArpResolution) {
FileDescriptor sender =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
+ const auto tuntap = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
sockaddr_in connect_addr = {
.sin_family = AF_INET,
@@ -443,8 +513,8 @@ TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) {
FileDescriptor sender = ASSERT_NO_ERRNO_AND_VALUE(
Socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP));
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
+ const auto tuntap = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
sockaddr_in connect_addr = {
.sin_family = AF_INET,
@@ -474,8 +544,8 @@ TEST_F(TuntapTest, TCPNonBlockingConnectFailsArpResolution) {
TEST_F(TuntapTest, WriteHangBug155928773) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
- FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTap(kTapName, kTapIPAddr));
+ const auto tuntap = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTapName, kTapIPAddr, true /* tap */, false /* no_pi */));
int sock = socket(AF_INET, SOCK_DGRAM, 0);
ASSERT_THAT(sock, SyscallSucceeds());
@@ -490,5 +560,95 @@ TEST_F(TuntapTest, WriteHangBug155928773) {
write(sock, "hello", 5);
}
+// Test that raw packet sockets do not need/include link headers when
+// sending/receiving packets to/from pure L3 (e.g. TUN) interfaces.
+TEST_F(TuntapTest, RawPacketSocket) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ auto [tun, link] = ASSERT_NO_ERRNO_AND_VALUE(OpenAndAttachTunTap(
+ kTunName, kTapIPAddr, false /* tap */, true /* no_pi */));
+ FileDescriptor packet_sock =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)));
+
+ constexpr int kInfiniteTimeout = -1;
+
+ uint8_t hardware_address_length = 0;
+ if (IsRunningOnGvisor()) {
+ // TODO(https://gvisor.dev/issue/6530): Do not assume all interfaces have
+ // an ethernet address.
+ hardware_address_length = ETH_ALEN;
+ }
+
+ {
+ const ping_ip_pkt ping_req = CreatePingIPPacket(kTapPeerIPAddr, kTapIPAddr);
+ ASSERT_THAT(write(tun.get(), &ping_req, sizeof(ping_req)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+ // Wait for the packet socket to become readable.
+ pollfd pfd = {
+ .fd = packet_sock.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfiniteTimeout),
+ SyscallSucceedsWithValue(1));
+
+ char read_buf[sizeof(ping_req) + 1];
+ struct sockaddr_ll src;
+ socklen_t src_len = sizeof(src);
+ ASSERT_THAT(recvfrom(packet_sock.get(), read_buf, sizeof(read_buf), 0,
+ reinterpret_cast<struct sockaddr*>(&src), &src_len),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+ EXPECT_EQ(memcmp(read_buf, &ping_req, sizeof(ping_req)), 0);
+ ASSERT_EQ(src_len, sizeof(src));
+ EXPECT_EQ(src.sll_family, AF_PACKET);
+ EXPECT_EQ(ntohs(src.sll_protocol), ETH_P_IP);
+ EXPECT_EQ(src.sll_ifindex, link.index);
+ EXPECT_EQ(src.sll_pkttype, PACKET_HOST);
+ EXPECT_EQ(src.sll_halen, hardware_address_length);
+ if (IsRunningOnGvisor()) {
+ // TODO(https://gvisor.dev/issue/6531): Check this field for the right
+ // hardware type.
+ EXPECT_EQ(src.sll_hatype, 0);
+ } else {
+ EXPECT_EQ(src.sll_hatype, ARPHRD_NONE);
+ }
+ }
+
+ {
+ const struct sockaddr_ll dest = {
+ .sll_family = AF_PACKET,
+ .sll_protocol = htons(ETH_P_IP),
+ .sll_ifindex = link.index,
+ .sll_halen = hardware_address_length,
+ };
+
+ const ping_ip_pkt ping_req = CreatePingIPPacket(kTapIPAddr, kTapPeerIPAddr);
+ ASSERT_THAT(
+ sendto(packet_sock.get(), &ping_req, sizeof(ping_req), 0,
+ reinterpret_cast<const struct sockaddr*>(&dest), sizeof(dest)),
+ SyscallSucceedsWithValue(sizeof(ping_req)));
+
+ // Loop until we receive the packet we expect - the kernel may send packets
+ // we do not care about.
+ while (true) {
+ // Wait for the TUN interface to become readable.
+ pollfd pfd = {
+ .fd = tun.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kInfiniteTimeout),
+ SyscallSucceedsWithValue(1));
+
+ char read_buf[sizeof(ping_req) + 1];
+ int n = read(tun.get(), &read_buf, sizeof(read_buf));
+ ASSERT_THAT(n, SyscallSucceeds());
+ if (n == sizeof(ping_req) &&
+ memcmp(read_buf, &ping_req, sizeof(ping_req)) == 0) {
+ break;
+ }
+ }
+ }
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/udp_bind.cc b/test/syscalls/linux/udp_bind.cc
index f68d78aa2..5ed115a14 100644
--- a/test/syscalls/linux/udp_bind.cc
+++ b/test/syscalls/linux/udp_bind.cc
@@ -17,8 +17,8 @@
#include <sys/types.h>
#include "gtest/gtest.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
namespace gvisor {
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 2b687c198..0c0a16074 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -18,6 +18,8 @@
#include <netinet/ip_icmp.h>
#include <ctime>
+#include <utility>
+#include <vector>
#ifdef __linux__
#include <linux/errqueue.h>
@@ -39,10 +41,10 @@
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
-#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/posix_error.h"
+#include "test/util/socket_util.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -600,6 +602,67 @@ TEST_P(UdpSocketTest, DisconnectAfterBind) {
SyscallFailsWithErrno(ENOTCONN));
}
+void ConnectThenDisconnect(const FileDescriptor& sock,
+ const sockaddr* bind_addr,
+ const socklen_t expected_addrlen) {
+ // Connect the bound socket.
+ ASSERT_THAT(connect(sock.get(), bind_addr, expected_addrlen),
+ SyscallSucceeds());
+
+ // Disconnect.
+ {
+ sockaddr_storage unspec = {.ss_family = AF_UNSPEC};
+ ASSERT_THAT(connect(sock.get(), AsSockAddr(&unspec), sizeof(unspec)),
+ SyscallSucceeds());
+ }
+ {
+ // Check that we're not in a bound state.
+ sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ ASSERT_THAT(getsockname(sock.get(), AsSockAddr(&addr), &addrlen),
+ SyscallSucceeds());
+ ASSERT_EQ(addrlen, expected_addrlen);
+ // Everything should be the zero value except the address family.
+ sockaddr_storage expected = {
+ .ss_family = bind_addr->sa_family,
+ };
+ EXPECT_EQ(memcmp(&expected, &addr, expected_addrlen), 0);
+ }
+
+ {
+ // We are not connected so we have no peer.
+ sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ EXPECT_THAT(getpeername(sock.get(), AsSockAddr(&addr), &addrlen),
+ SyscallFailsWithErrno(ENOTCONN));
+ }
+}
+
+TEST_P(UdpSocketTest, DisconnectAfterBindToUnspecAndConnect) {
+ ASSERT_NO_ERRNO(BindLoopback());
+
+ sockaddr_storage unspec = {.ss_family = AF_UNSPEC};
+ int bind_res = bind(sock_.get(), AsSockAddr(&unspec), sizeof(unspec));
+ if ((!IsRunningOnGvisor() || IsRunningWithHostinet()) &&
+ GetFamily() == AF_INET) {
+ // Linux allows this for undocumented compatibility reasons:
+ // https://github.com/torvalds/linux/commit/29c486df6a208432b370bd4be99ae1369ede28d8.
+ //
+ // TODO(https://gvisor.dev/issue/6575): Match Linux's behaviour.
+ ASSERT_THAT(bind_res, SyscallSucceeds());
+ } else {
+ ASSERT_THAT(bind_res, SyscallFailsWithErrno(EAFNOSUPPORT));
+ }
+
+ ASSERT_NO_FATAL_FAILURE(ConnectThenDisconnect(sock_, bind_addr_, addrlen_));
+}
+
+TEST_P(UdpSocketTest, DisconnectAfterConnectWithoutBind) {
+ ASSERT_NO_ERRNO(BindLoopback());
+
+ ASSERT_NO_FATAL_FAILURE(ConnectThenDisconnect(sock_, bind_addr_, addrlen_));
+}
+
TEST_P(UdpSocketTest, BindToAnyConnnectToLocalhost) {
ASSERT_NO_ERRNO(BindAny());
@@ -1429,12 +1492,8 @@ TEST_P(UdpSocketTest, FIONREADZeroLengthPacket) {
sendto(sock_.get(), buf + i * psize, 0, 0, bind_addr_, addrlen_),
SyscallSucceedsWithValue(0));
- // TODO(gvisor.dev/issue/2726): sending a zero-length message to a hostinet
- // socket does not cause a poll event to be triggered.
- if (!IsRunningWithHostinet()) {
- ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000),
- SyscallSucceedsWithValue(1));
- }
+ ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, /*timeout=*/1000),
+ SyscallSucceedsWithValue(1));
// Check that regardless of how many packets are in the queue, the size
// reported is that of a single packet.
@@ -1689,6 +1748,148 @@ TEST_P(UdpSocketTest, TimestampIoctlPersistence) {
ASSERT_EQ(tv.tv_usec, tv2.tv_usec);
}
+// TOS and TCLASS values may be different but IPv6 sockets with IPv4-mapped-IPv6
+// addresses use TOS (IPv4), not TCLASS (IPv6).
+TEST_P(UdpSocketTest, DifferentTOSAndTClass) {
+ const int kFamily = GetFamily();
+ constexpr int kToS = IPTOS_LOWDELAY;
+ constexpr int kTClass = IPTOS_THROUGHPUT;
+ ASSERT_NE(kToS, kTClass);
+
+ if (kFamily == AF_INET6) {
+ ASSERT_THAT(setsockopt(sock_.get(), SOL_IPV6, IPV6_TCLASS, &kTClass,
+ sizeof(kTClass)),
+ SyscallSucceeds());
+
+ // Marking an IPv6 socket as IPv6 only should not affect the ability to
+ // configure IPv4 socket options as the V6ONLY flag may later be disabled so
+ // that applications may use the socket to send/receive IPv4 packets.
+ constexpr int on = 1;
+ ASSERT_THAT(setsockopt(sock_.get(), SOL_IPV6, IPV6_V6ONLY, &on, sizeof(on)),
+ SyscallSucceeds());
+ }
+
+ ASSERT_THAT(setsockopt(sock_.get(), SOL_IP, IP_TOS, &kToS, sizeof(kToS)),
+ SyscallSucceeds());
+
+ if (kFamily == AF_INET6) {
+ int got_tclass;
+ socklen_t got_tclass_len = sizeof(got_tclass);
+ ASSERT_THAT(getsockopt(sock_.get(), SOL_IPV6, IPV6_TCLASS, &got_tclass,
+ &got_tclass_len),
+ SyscallSucceeds());
+ ASSERT_EQ(got_tclass_len, sizeof(got_tclass));
+ EXPECT_EQ(got_tclass, kTClass);
+ }
+
+ {
+ int got_tos;
+ socklen_t got_tos_len = sizeof(got_tos);
+ ASSERT_THAT(getsockopt(sock_.get(), SOL_IP, IP_TOS, &got_tos, &got_tos_len),
+ SyscallSucceeds());
+ ASSERT_EQ(got_tos_len, sizeof(got_tos));
+ EXPECT_EQ(got_tos, kToS);
+ }
+
+ auto test_send = [this](sockaddr_storage addr,
+ std::function<void(const cmsghdr*)> cb) {
+ FileDescriptor bind = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(addr.ss_family, SOCK_DGRAM, IPPROTO_UDP));
+ ASSERT_NO_ERRNO(BindSocket(bind.get(), reinterpret_cast<sockaddr*>(&addr)));
+ ASSERT_THAT(setsockopt(bind.get(), SOL_IP, IP_RECVTOS, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ if (addr.ss_family == AF_INET6) {
+ ASSERT_THAT(setsockopt(bind.get(), SOL_IPV6, IPV6_RECVTCLASS, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ }
+
+ char sent_data[1024];
+ iovec sent_iov = {
+ .iov_base = sent_data,
+ .iov_len = sizeof(sent_data),
+ };
+ msghdr sent_msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(addr),
+ .msg_iov = &sent_iov,
+ .msg_iovlen = 1,
+ };
+ ASSERT_THAT(RetryEINTR(sendmsg)(sock_.get(), &sent_msg, 0),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ char received_data[sizeof(sent_data) + 1];
+ iovec received_iov = {
+ .iov_base = received_data,
+ .iov_len = sizeof(received_data),
+ };
+ std::vector<char> received_cmsgbuf(CMSG_SPACE(sizeof(int8_t)));
+ msghdr received_msg = {
+ .msg_iov = &received_iov,
+ .msg_iovlen = 1,
+ .msg_control = received_cmsgbuf.data(),
+ .msg_controllen = static_cast<socklen_t>(received_cmsgbuf.size()),
+ };
+ ASSERT_THAT(RetryEINTR(recvmsg)(bind.get(), &received_msg, 0),
+ SyscallSucceedsWithValue(sizeof(sent_data)));
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
+ ASSERT_NE(cmsg, nullptr);
+ ASSERT_NO_FATAL_FAILURE(cb(cmsg));
+ EXPECT_EQ(CMSG_NXTHDR(&received_msg, cmsg), nullptr);
+ };
+
+ if (kFamily == AF_INET6) {
+ SCOPED_TRACE(
+ "Send IPv4 loopback packet using IPv6 socket via IPv4-mapped-IPv6");
+
+ constexpr int off = 0;
+ ASSERT_THAT(
+ setsockopt(sock_.get(), SOL_IPV6, IPV6_V6ONLY, &off, sizeof(off)),
+ SyscallSucceeds());
+
+ // Send a packet and make sure that the ToS value in the IPv4 header is
+ // the configured IPv4 ToS Value and not the IPv6 Traffic Class value even
+ // though we use an IPv6 socket to send an IPv4 packet.
+ ASSERT_NO_FATAL_FAILURE(
+ test_send(V4MappedLoopback().addr, [kToS](const cmsghdr* cmsg) {
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int8_t)));
+ EXPECT_EQ(cmsg->cmsg_level, SOL_IP);
+ EXPECT_EQ(cmsg->cmsg_type, IP_TOS);
+ int8_t received;
+ memcpy(&received, CMSG_DATA(cmsg), sizeof(received));
+ EXPECT_EQ(received, kToS);
+ }));
+ }
+
+ {
+ SCOPED_TRACE("Send loopback packet");
+
+ ASSERT_NO_FATAL_FAILURE(test_send(
+ InetLoopbackAddr(), [kFamily, kTClass, kToS](const cmsghdr* cmsg) {
+ switch (kFamily) {
+ case AF_INET: {
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int8_t)));
+ EXPECT_EQ(cmsg->cmsg_level, SOL_IP);
+ EXPECT_EQ(cmsg->cmsg_type, IP_TOS);
+ int8_t received;
+ memcpy(&received, CMSG_DATA(cmsg), sizeof(received));
+ EXPECT_EQ(received, kToS);
+ } break;
+ case AF_INET6: {
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(int32_t)));
+ EXPECT_EQ(cmsg->cmsg_level, SOL_IPV6);
+ EXPECT_EQ(cmsg->cmsg_type, IPV6_TCLASS);
+ int32_t received;
+ memcpy(&received, CMSG_DATA(cmsg), sizeof(received));
+ EXPECT_EQ(received, kTClass);
+ } break;
+ }
+ }));
+ }
+}
+
// Test that a socket with IP_TOS or IPV6_TCLASS set will set the TOS byte on
// outgoing packets, and that a receiving socket with IP_RECVTOS or
// IPV6_RECVTCLASS will create the corresponding control message.
diff --git a/test/syscalls/linux/uidgid.cc b/test/syscalls/linux/uidgid.cc
index 4139a18d8..d95a3e010 100644
--- a/test/syscalls/linux/uidgid.cc
+++ b/test/syscalls/linux/uidgid.cc
@@ -170,7 +170,9 @@ TEST(UidGidRootTest, SetgidNotFromThreadGroupLeader) {
const gid_t gid = absl::GetFlag(FLAGS_scratch_gid1);
// NOTE(b/64676707): Do setgid in a separate thread so that we can test if
// info.si_pid is set correctly.
- ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); });
+ ScopedThread thread =
+ ScopedThread([gid] { ASSERT_THAT(setgid(gid), SyscallSucceeds()); });
+ thread.Join();
EXPECT_NO_ERRNO(CheckGIDs(gid, gid, gid));
#pragma pop_macro("allow_setgid")
diff --git a/test/syscalls/linux/uname.cc b/test/syscalls/linux/uname.cc
index 759ea4f53..c52abef5c 100644
--- a/test/syscalls/linux/uname.cc
+++ b/test/syscalls/linux/uname.cc
@@ -88,7 +88,7 @@ TEST(UnameTest, UnshareUTS) {
struct utsname init;
ASSERT_THAT(uname(&init), SyscallSucceeds());
- ScopedThread([&]() {
+ ScopedThread thread = ScopedThread([&]() {
EXPECT_THAT(unshare(CLONE_NEWUTS), SyscallSucceeds());
constexpr char kHostname[] = "wubbalubba";
@@ -97,6 +97,7 @@ TEST(UnameTest, UnshareUTS) {
char hostname[65];
EXPECT_THAT(gethostname(hostname, sizeof(hostname)), SyscallSucceeds());
});
+ thread.Join();
struct utsname after;
EXPECT_THAT(uname(&after), SyscallSucceeds());
diff --git a/test/syscalls/linux/unix_domain_socket_test_util.h b/test/syscalls/linux/unix_domain_socket_test_util.h
index b8073db17..4240bd5f6 100644
--- a/test/syscalls/linux/unix_domain_socket_test_util.h
+++ b/test/syscalls/linux/unix_domain_socket_test_util.h
@@ -17,7 +17,7 @@
#include <string>
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/syscalls/linux/verity_getdents.cc b/test/syscalls/linux/verity_getdents.cc
index 093595dd3..822a75254 100644
--- a/test/syscalls/linux/verity_getdents.cc
+++ b/test/syscalls/linux/verity_getdents.cc
@@ -58,16 +58,16 @@ class GetDentsTest : public ::testing::Test {
};
TEST_F(GetDentsTest, GetDents) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
std::vector<std::string> expect = {".", "..", filename_};
EXPECT_NO_ERRNO(DirContains(verity_dir, expect, /*exclude=*/{}));
}
TEST_F(GetDentsTest, Deleted) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
EXPECT_THAT(unlink(JoinPath(tmpfs_dir_.path(), filename_).c_str()),
SyscallSucceeds());
@@ -77,8 +77,8 @@ TEST_F(GetDentsTest, Deleted) {
}
TEST_F(GetDentsTest, Renamed) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
std::string new_file_name = "renamed-" + filename_;
EXPECT_THAT(rename(JoinPath(tmpfs_dir_.path(), filename_).c_str(),
diff --git a/test/syscalls/linux/verity_ioctl.cc b/test/syscalls/linux/verity_ioctl.cc
index be91b23d0..45650809c 100644
--- a/test/syscalls/linux/verity_ioctl.cc
+++ b/test/syscalls/linux/verity_ioctl.cc
@@ -105,8 +105,8 @@ TEST_F(IoctlTest, Measure) {
}
TEST_F(IoctlTest, Mount) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Make sure the file can be open and read in the mounted verity fs.
auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -117,8 +117,8 @@ TEST_F(IoctlTest, Mount) {
}
TEST_F(IoctlTest, NonExistingFile) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Confirm that opening a non-existing file in the verity-enabled directory
// triggers the expected error instead of verification failure.
@@ -128,8 +128,8 @@ TEST_F(IoctlTest, NonExistingFile) {
}
TEST_F(IoctlTest, ModifiedFile) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Modify the file and check verification failure upon reading from it.
auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -143,8 +143,8 @@ TEST_F(IoctlTest, ModifiedFile) {
}
TEST_F(IoctlTest, ModifiedMerkle) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Modify the Merkle file and check verification failure upon opening the
// corresponding file.
@@ -158,8 +158,8 @@ TEST_F(IoctlTest, ModifiedMerkle) {
}
TEST_F(IoctlTest, ModifiedDirMerkle) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Modify the Merkle file for the parent directory and check verification
// failure upon opening the corresponding file.
@@ -173,8 +173,8 @@ TEST_F(IoctlTest, ModifiedDirMerkle) {
}
TEST_F(IoctlTest, Stat) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
struct stat st;
EXPECT_THAT(stat(JoinPath(verity_dir, filename_).c_str(), &st),
@@ -182,8 +182,8 @@ TEST_F(IoctlTest, Stat) {
}
TEST_F(IoctlTest, ModifiedStat) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
EXPECT_THAT(chmod(JoinPath(tmpfs_dir_.path(), filename_).c_str(), 0644),
SyscallSucceeds());
@@ -193,8 +193,8 @@ TEST_F(IoctlTest, ModifiedStat) {
}
TEST_F(IoctlTest, DeleteFile) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
EXPECT_THAT(unlink(JoinPath(tmpfs_dir_.path(), filename_).c_str()),
SyscallSucceeds());
@@ -203,8 +203,8 @@ TEST_F(IoctlTest, DeleteFile) {
}
TEST_F(IoctlTest, DeleteMerkle) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
EXPECT_THAT(
unlink(MerklePath(JoinPath(tmpfs_dir_.path(), filename_)).c_str()),
@@ -214,8 +214,8 @@ TEST_F(IoctlTest, DeleteMerkle) {
}
TEST_F(IoctlTest, RenameFile) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
std::string new_file_name = "renamed-" + filename_;
EXPECT_THAT(rename(JoinPath(tmpfs_dir_.path(), filename_).c_str(),
@@ -226,8 +226,8 @@ TEST_F(IoctlTest, RenameFile) {
}
TEST_F(IoctlTest, RenameMerkle) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
std::string new_file_name = "renamed-" + filename_;
EXPECT_THAT(
diff --git a/test/syscalls/linux/verity_mmap.cc b/test/syscalls/linux/verity_mmap.cc
index dde74cc91..2bfd43b16 100644
--- a/test/syscalls/linux/verity_mmap.cc
+++ b/test/syscalls/linux/verity_mmap.cc
@@ -57,8 +57,8 @@ class MmapTest : public ::testing::Test {
};
TEST_F(MmapTest, MmapRead) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Make sure the file can be open and mmapped in the mounted verity fs.
auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -71,8 +71,8 @@ TEST_F(MmapTest, MmapRead) {
}
TEST_F(MmapTest, ModifiedBeforeMmap) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Modify the file and check verification failure upon mmapping.
auto const fd = ASSERT_NO_ERRNO_AND_VALUE(
@@ -90,8 +90,8 @@ TEST_F(MmapTest, ModifiedBeforeMmap) {
}
TEST_F(MmapTest, ModifiedAfterMmap) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777));
@@ -126,8 +126,8 @@ INSTANTIATE_TEST_SUITE_P(
::testing::ValuesIn({MAP_SHARED, MAP_PRIVATE})));
TEST_P(MmapParamTest, Mmap) {
- std::string verity_dir =
- ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_));
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(
+ MountVerity(tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY)}));
// Make sure the file can be open and mmapped in the mounted verity fs.
auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
diff --git a/test/syscalls/linux/verity_symlink.cc b/test/syscalls/linux/verity_symlink.cc
new file mode 100644
index 000000000..c6fce8ead
--- /dev/null
+++ b/test/syscalls/linux/verity_symlink.cc
@@ -0,0 +1,117 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "test/util/capability_util.h"
+#include "test/util/fs_util.h"
+#include "test/util/mount_util.h"
+#include "test/util/temp_path.h"
+#include "test/util/test_util.h"
+#include "test/util/verity_util.h"
+
+namespace gvisor {
+namespace testing {
+
+namespace {
+
+const char kSymlink[] = "verity_symlink";
+
+class SymlinkTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ // Verity is implemented in VFS2.
+ SKIP_IF(IsRunningWithVFS1());
+
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
+ // Mount a tmpfs file system, to be wrapped by a verity fs.
+ tmpfs_dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ ASSERT_THAT(mount("", tmpfs_dir_.path().c_str(), "tmpfs", 0, ""),
+ SyscallSucceeds());
+
+ // Create a new file in the tmpfs mount.
+ file_ = ASSERT_NO_ERRNO_AND_VALUE(
+ TempPath::CreateFileWith(tmpfs_dir_.path(), kContents, 0777));
+ filename_ = Basename(file_.path());
+
+ // Create a symlink to the file.
+ ASSERT_THAT(symlink(file_.path().c_str(),
+ JoinPath(tmpfs_dir_.path(), kSymlink).c_str()),
+ SyscallSucceeds());
+ }
+
+ TempPath tmpfs_dir_;
+ TempPath file_;
+ std::string filename_;
+};
+
+TEST_F(SymlinkTest, Success) {
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(MountVerity(
+ tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY),
+ EnableTarget(kSymlink, O_RDONLY | O_NOFOLLOW)}));
+
+ char buf[256];
+ EXPECT_THAT(
+ readlink(JoinPath(verity_dir, kSymlink).c_str(), buf, sizeof(buf)),
+ SyscallSucceeds());
+ auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Open(JoinPath(verity_dir, kSymlink).c_str(), O_RDONLY, 0777));
+ EXPECT_THAT(ReadFd(verity_fd.get(), buf, sizeof(kContents)),
+ SyscallSucceeds());
+}
+
+TEST_F(SymlinkTest, DeleteLink) {
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(MountVerity(
+ tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY),
+ EnableTarget(kSymlink, O_RDONLY | O_NOFOLLOW)}));
+
+ ASSERT_THAT(unlink(JoinPath(tmpfs_dir_.path(), kSymlink).c_str()),
+ SyscallSucceeds());
+ char buf[256];
+ EXPECT_THAT(
+ readlink(JoinPath(verity_dir, kSymlink).c_str(), buf, sizeof(buf)),
+ SyscallFailsWithErrno(EIO));
+ EXPECT_THAT(open(JoinPath(verity_dir, kSymlink).c_str(), O_RDONLY, 0777),
+ SyscallFailsWithErrno(EIO));
+}
+
+TEST_F(SymlinkTest, ModifyLink) {
+ std::string verity_dir = ASSERT_NO_ERRNO_AND_VALUE(MountVerity(
+ tmpfs_dir_.path(), {EnableTarget(filename_, O_RDONLY),
+ EnableTarget(kSymlink, O_RDONLY | O_NOFOLLOW)}));
+
+ ASSERT_THAT(unlink(JoinPath(tmpfs_dir_.path(), kSymlink).c_str()),
+ SyscallSucceeds());
+
+ std::string newlink = "newlink";
+ ASSERT_THAT(symlink(JoinPath(tmpfs_dir_.path(), newlink).c_str(),
+ JoinPath(tmpfs_dir_.path(), kSymlink).c_str()),
+ SyscallSucceeds());
+ char buf[256];
+ EXPECT_THAT(
+ readlink(JoinPath(verity_dir, kSymlink).c_str(), buf, sizeof(buf)),
+ SyscallFailsWithErrno(EIO));
+ EXPECT_THAT(open(JoinPath(verity_dir, kSymlink).c_str(), O_RDONLY, 0777),
+ SyscallFailsWithErrno(EIO));
+}
+
+} // namespace
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/util/BUILD b/test/util/BUILD
index cc83221ea..5b6bcb32c 100644
--- a/test/util/BUILD
+++ b/test/util/BUILD
@@ -1,4 +1,4 @@
-load("//tools:defs.bzl", "cc_library", "cc_test", "coreutil", "gbenchmark", "gtest", "select_system")
+load("//tools:defs.bzl", "cc_library", "cc_test", "coreutil", "default_net_util", "gbenchmark_internal", "gtest", "select_system")
package(
default_visibility = ["//:sandbox"],
@@ -8,13 +8,20 @@ package(
cc_library(
name = "capability_util",
testonly = 1,
- srcs = ["capability_util.cc"],
- hdrs = ["capability_util.h"],
+ srcs = [
+ "fuchsia_capability_util.cc",
+ "linux_capability_util.cc",
+ ],
+ hdrs = [
+ "capability_util.h",
+ "linux_capability_util.h",
+ ],
deps = [
":cleanup",
":memory_util",
":posix_error",
":save_util",
+ ":socket_util",
":test_util",
"@com_google_absl//absl/strings",
],
@@ -288,7 +295,7 @@ cc_library(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
gtest,
- gbenchmark,
+ gbenchmark_internal,
],
)
@@ -343,6 +350,18 @@ cc_library(
)
cc_library(
+ name = "benchmark_main",
+ testonly = 1,
+ linkstatic = 1,
+ deps = [
+ ":test_util",
+ "@com_google_absl//absl/flags:flag",
+ gtest,
+ gbenchmark_internal,
+ ],
+)
+
+cc_library(
name = "epoll_util",
testonly = 1,
srcs = ["epoll_util.cc"],
@@ -414,3 +433,27 @@ cc_library(
":temp_path",
],
)
+
+cc_library(
+ name = "socket_util",
+ testonly = 1,
+ srcs = [
+ "socket_util.cc",
+ "socket_util_impl.cc",
+ ],
+ hdrs = ["socket_util.h"],
+ defines = select_system(),
+ deps = default_net_util() + [
+ gtest,
+ "@com_google_absl//absl/memory",
+ "@com_google_absl//absl/strings",
+ "@com_google_absl//absl/strings:str_format",
+ "@com_google_absl//absl/time",
+ "@com_google_absl//absl/types:optional",
+ "//test/util:file_descriptor",
+ "//test/util:posix_error",
+ "//test/util:temp_path",
+ "//test/util:test_util",
+ "//test/util:thread_util",
+ ],
+)
diff --git a/test/util/capability_util.h b/test/util/capability_util.h
index f2c370125..ac1a1b32b 100644
--- a/test/util/capability_util.h
+++ b/test/util/capability_util.h
@@ -17,107 +17,32 @@
#ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
#define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
-#include <errno.h>
-#include <linux/capability.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include "test/util/cleanup.h"
#include "test/util/posix_error.h"
-#include "test/util/save_util.h"
-#include "test/util/test_util.h"
-#ifndef _LINUX_CAPABILITY_VERSION_3
-#error Expecting _LINUX_CAPABILITY_VERSION_3 support
+#if defined(__Fuchsia__)
+// Nothing to include.
+#elif defined(__linux__)
+#include "test/util/linux_capability_util.h"
+#else
+#error "Unhandled platform"
#endif
namespace gvisor {
namespace testing {
-// HaveCapability returns true if the process has the specified EFFECTIVE
-// capability.
-inline PosixErrorOr<bool> HaveCapability(int cap) {
- if (!cap_valid(cap)) {
- return PosixError(EINVAL, "Invalid capability");
- }
-
- struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
- struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
- MaybeSave();
-
- return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0;
-}
-
-// SetCapability sets the specified EFFECTIVE capability.
-inline PosixError SetCapability(int cap, bool set) {
- if (!cap_valid(cap)) {
- return PosixError(EINVAL, "Invalid capability");
- }
-
- struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
- struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
- MaybeSave();
-
- if (set) {
- caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap);
- } else {
- caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
- }
- header = {_LINUX_CAPABILITY_VERSION_3, 0};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
- MaybeSave();
-
- return NoError();
-}
-
-// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE
-// capabilities must be a subset of PERMITTED, so those are dropped as well.
-inline PosixError DropPermittedCapability(int cap) {
- if (!cap_valid(cap)) {
- return PosixError(EINVAL, "Invalid capability");
- }
-
- struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
- struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
- MaybeSave();
-
- caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
- caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap);
-
- header = {_LINUX_CAPABILITY_VERSION_3, 0};
- RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
- MaybeSave();
-
- return NoError();
-}
-
-PosixErrorOr<bool> CanCreateUserNamespace();
-
-class AutoCapability {
- public:
- AutoCapability(int cap, bool set) : cap_(cap), set_(set) {
- const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap));
- if (set != has) {
- EXPECT_NO_ERRNO(SetCapability(cap_, set_));
- applied_ = true;
- }
- }
-
- ~AutoCapability() {
- if (applied_) {
- EXPECT_NO_ERRNO(SetCapability(cap_, !set_));
- }
- }
+// HaveRawIPSocketCapability returns whether or not the process has access to
+// raw IP sockets.
+//
+// Returns an error when raw IP socket access cannot be determined.
+PosixErrorOr<bool> HaveRawIPSocketCapability();
- private:
- int cap_;
- bool set_;
- bool applied_ = false;
-};
+// HavePacketSocketCapability returns whether or not the process has access to
+// packet sockets.
+//
+// Returns an error when packet socket access cannot be determined.
+PosixErrorOr<bool> HavePacketSocketCapability();
} // namespace testing
} // namespace gvisor
+
#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
diff --git a/test/util/cgroup_util.cc b/test/util/cgroup_util.cc
index 977993f41..df3c57b87 100644
--- a/test/util/cgroup_util.cc
+++ b/test/util/cgroup_util.cc
@@ -25,12 +25,26 @@
namespace gvisor {
namespace testing {
-Cgroup::Cgroup(std::string_view path) : cgroup_path_(path) {
+Cgroup::Cgroup(absl::string_view path) : cgroup_path_(path) {
id_ = ++Cgroup::next_id_;
std::cerr << absl::StreamFormat("[cg#%d] <= %s", id_, cgroup_path_)
<< std::endl;
}
+PosixErrorOr<Cgroup> Cgroup::RecursivelyCreate(absl::string_view path) {
+ RETURN_IF_ERRNO(RecursivelyCreateDir(path));
+ return Cgroup(path);
+}
+
+PosixErrorOr<Cgroup> Cgroup::Create(absl::string_view path) {
+ RETURN_IF_ERRNO(Mkdir(path));
+ return Cgroup(path);
+}
+
+PosixErrorOr<Cgroup> Cgroup::CreateChild(absl::string_view name) const {
+ return Cgroup::Create(JoinPath(Path(), name));
+}
+
PosixErrorOr<std::string> Cgroup::ReadControlFile(
absl::string_view name) const {
std::string buf;
@@ -93,7 +107,7 @@ PosixErrorOr<absl::flat_hash_set<pid_t>> Cgroup::ParsePIDList(
absl::string_view data) const {
absl::flat_hash_set<pid_t> res;
std::vector<absl::string_view> lines = absl::StrSplit(data, '\n');
- for (const std::string_view& line : lines) {
+ for (const absl::string_view& line : lines) {
if (line.empty()) {
continue;
}
diff --git a/test/util/cgroup_util.h b/test/util/cgroup_util.h
index e3f696a89..ccc7219e3 100644
--- a/test/util/cgroup_util.h
+++ b/test/util/cgroup_util.h
@@ -34,8 +34,20 @@ class Cgroup {
uint64_t id() const { return id_; }
+ // RecursivelyCreate creates cgroup specified by path, including all
+ // components leading up to path. Path should end inside a cgroupfs mount. If
+ // path already exists, RecursivelyCreate does nothing and silently succeeds.
+ static PosixErrorOr<Cgroup> RecursivelyCreate(std::string_view path);
+
+ // Creates a new cgroup at path. The parent directory must exist and be a
+ // cgroupfs directory.
+ static PosixErrorOr<Cgroup> Create(std::string_view path);
+
const std::string& Path() const { return cgroup_path_; }
+ // Creates a child cgroup under this cgroup with the given name.
+ PosixErrorOr<Cgroup> CreateChild(std::string_view name) const;
+
std::string Relpath(absl::string_view leaf) const {
return JoinPath(cgroup_path_, leaf);
}
diff --git a/test/util/fs_util.cc b/test/util/fs_util.cc
index 483ae848d..253411858 100644
--- a/test/util/fs_util.cc
+++ b/test/util/fs_util.cc
@@ -201,7 +201,8 @@ PosixError UnlinkAt(const FileDescriptor& dfd, absl::string_view path,
PosixError Mkdir(absl::string_view path, int mode) {
int res = mkdir(std::string(path).c_str(), mode);
if (res < 0) {
- return PosixError(errno, absl::StrCat("mkdir ", path, " mode ", mode));
+ return PosixError(errno,
+ absl::StrFormat("mkdir \"%s\" mode %#o", path, mode));
}
return NoError();
diff --git a/test/util/fuchsia_capability_util.cc b/test/util/fuchsia_capability_util.cc
new file mode 100644
index 000000000..bbe8643e7
--- /dev/null
+++ b/test/util/fuchsia_capability_util.cc
@@ -0,0 +1,73 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifdef __Fuchsia__
+
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include "test/util/socket_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// On Linux, access to raw IP and packet socket is controlled by a single
+// capability (CAP_NET_RAW). However on Fuchsia, access to raw IP and packet
+// sockets are controlled by separate capabilities/protocols.
+
+namespace {
+
+PosixErrorOr<bool> HaveSocketCapability(int domain, int type, int protocol) {
+ // Fuchsia does not have a platform supported way to check the protocols made
+ // available to a sandbox. As a workaround, simply try to create the specified
+ // socket and assume no access if we get a no permissions error.
+ auto s = Socket(domain, type, protocol);
+ if (s.ok()) {
+ return true;
+ }
+ if (s.error().errno_value() == EPERM) {
+ return false;
+ }
+ return s.error();
+}
+
+} // namespace
+
+PosixErrorOr<bool> HaveRawIPSocketCapability() {
+ static PosixErrorOr<bool> result(false);
+ static std::once_flag once;
+
+ std::call_once(once, [&]() {
+ result = HaveSocketCapability(AF_INET, SOCK_RAW, IPPROTO_UDP);
+ });
+
+ return result;
+}
+
+PosixErrorOr<bool> HavePacketSocketCapability() {
+ static PosixErrorOr<bool> result(false);
+ static std::once_flag once;
+
+ std::call_once(once, [&]() {
+ result = HaveSocketCapability(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ });
+
+ return result;
+}
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // __Fuchsia__
diff --git a/test/util/capability_util.cc b/test/util/linux_capability_util.cc
index a1b994c45..7218aa4ac 100644
--- a/test/util/capability_util.cc
+++ b/test/util/linux_capability_util.cc
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "test/util/capability_util.h"
+#ifdef __linux__
+
+#include "test/util/linux_capability_util.h"
#include <linux/capability.h>
#include <sched.h>
@@ -30,6 +32,14 @@
namespace gvisor {
namespace testing {
+PosixErrorOr<bool> HaveRawIPSocketCapability() {
+ return HaveCapability(CAP_NET_RAW);
+}
+
+PosixErrorOr<bool> HavePacketSocketCapability() {
+ return HaveCapability(CAP_NET_RAW);
+}
+
PosixErrorOr<bool> CanCreateUserNamespace() {
// The most reliable way to determine if userns creation is possible is by
// trying to create one; see below.
@@ -79,3 +89,5 @@ PosixErrorOr<bool> CanCreateUserNamespace() {
} // namespace testing
} // namespace gvisor
+
+#endif // __linux__
diff --git a/test/util/linux_capability_util.h b/test/util/linux_capability_util.h
new file mode 100644
index 000000000..be94ebd19
--- /dev/null
+++ b/test/util/linux_capability_util.h
@@ -0,0 +1,128 @@
+// Copyright 2018 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Utilities for testing capabilities on Linux.
+
+#ifndef GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_
+#define GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_
+
+#ifdef __linux__
+
+#include <errno.h>
+#include <linux/capability.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "test/util/cleanup.h"
+#include "test/util/posix_error.h"
+#include "test/util/save_util.h"
+#include "test/util/test_util.h"
+
+#ifndef _LINUX_CAPABILITY_VERSION_3
+#error Expecting _LINUX_CAPABILITY_VERSION_3 support
+#endif
+
+namespace gvisor {
+namespace testing {
+
+// HaveCapability returns true if the process has the specified EFFECTIVE
+// capability.
+inline PosixErrorOr<bool> HaveCapability(int cap) {
+ if (!cap_valid(cap)) {
+ return PosixError(EINVAL, "Invalid capability");
+ }
+
+ struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
+ MaybeSave();
+
+ return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0;
+}
+
+// SetCapability sets the specified EFFECTIVE capability.
+inline PosixError SetCapability(int cap, bool set) {
+ if (!cap_valid(cap)) {
+ return PosixError(EINVAL, "Invalid capability");
+ }
+
+ struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
+ MaybeSave();
+
+ if (set) {
+ caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap);
+ } else {
+ caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
+ }
+ header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
+ MaybeSave();
+
+ return NoError();
+}
+
+// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE
+// capabilities must be a subset of PERMITTED, so those are dropped as well.
+inline PosixError DropPermittedCapability(int cap) {
+ if (!cap_valid(cap)) {
+ return PosixError(EINVAL, "Invalid capability");
+ }
+
+ struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps));
+ MaybeSave();
+
+ caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap);
+ caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap);
+
+ header = {_LINUX_CAPABILITY_VERSION_3, 0};
+ RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps));
+ MaybeSave();
+
+ return NoError();
+}
+
+PosixErrorOr<bool> CanCreateUserNamespace();
+
+class AutoCapability {
+ public:
+ AutoCapability(int cap, bool set) : cap_(cap), set_(set) {
+ const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap));
+ if (set != has) {
+ EXPECT_NO_ERRNO(SetCapability(cap_, set_));
+ applied_ = true;
+ }
+ }
+
+ ~AutoCapability() {
+ if (applied_) {
+ EXPECT_NO_ERRNO(SetCapability(cap_, !set_));
+ }
+ }
+
+ private:
+ int cap_;
+ bool set_;
+ bool applied_ = false;
+};
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // __linux__
+
+#endif // GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_
diff --git a/test/util/posix_error.h b/test/util/posix_error.h
index 9ca09b77c..40853cb21 100644
--- a/test/util/posix_error.h
+++ b/test/util/posix_error.h
@@ -385,7 +385,7 @@ class PosixErrorIsMatcher {
};
// Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose
-// whose error code matches code_matcher, and whose error message matches
+// error code matches code_matcher, and whose error message matches
// message_matcher.
template <typename ErrorCodeMatcher>
PosixErrorIsMatcher PosixErrorIs(
@@ -395,6 +395,14 @@ PosixErrorIsMatcher PosixErrorIs(
std::move(message_matcher));
}
+// Returns a gMock matcher that matches a PosixError or PosixErrorOr<> whose
+// error code matches code_matcher.
+template <typename ErrorCodeMatcher>
+PosixErrorIsMatcher PosixErrorIs(ErrorCodeMatcher&& code_matcher) {
+ return PosixErrorIsMatcher(std::forward<ErrorCodeMatcher>(code_matcher),
+ ::testing::_);
+}
+
// Returns a gMock matcher that matches a PosixErrorOr<> which is ok() and
// value matches the inner matcher.
template <typename InnerMatcher>
diff --git a/test/syscalls/linux/socket_test_util.cc b/test/util/socket_util.cc
index 6039b2a67..650b422ae 100644
--- a/test/syscalls/linux/socket_test_util.cc
+++ b/test/util/socket_util.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
#include <arpa/inet.h>
#include <netinet/in.h>
@@ -24,6 +24,7 @@
#include "gtest/gtest.h"
#include "absl/memory/memory.h"
#include "absl/strings/str_cat.h"
+#include "absl/strings/str_split.h"
#include "absl/time/clock.h"
#include "absl/types/optional.h"
#include "test/util/file_descriptor.h"
@@ -56,7 +57,19 @@ Creator<FileDescriptor> SyscallSocketCreator(int domain, int type,
PosixErrorOr<struct sockaddr_un> UniqueUnixAddr(bool abstract, int domain) {
struct sockaddr_un addr = {};
+
+#ifdef ANDROID
+ // Using NewTempAbsPath() can cause the tmp directory path to exceed the max
+ // length (i.e., sizeof(addr.sun_path)).
+ //
+ // However, existing systems that are built with the ANDROID configuration
+ // have their temp directory in a different location, and must respect the
+ // TEST_TMPDIR.
+ std::string path = NewTempAbsPath();
+#else
std::string path = NewTempAbsPathInDir("/tmp");
+#endif // ANDROID
+
if (path.size() >= sizeof(addr.sun_path)) {
return PosixError(EINVAL,
"Unable to generate a temp path of appropriate length");
@@ -85,7 +98,8 @@ Creator<SocketPair> AcceptBindSocketPairCreator(bool abstract, int domain,
RETURN_ERROR_IF_SYSCALL_FAIL(
bind(bound, AsSockAddr(&bind_addr), sizeof(bind_addr)));
MaybeSave(); // Successful bind.
- RETURN_ERROR_IF_SYSCALL_FAIL(listen(bound, /* backlog = */ 5));
+ RETURN_ERROR_IF_SYSCALL_FAIL(
+ listen(bound, /* backlog = */ 5)); // NOLINT(bugprone-argument-comment)
MaybeSave(); // Successful listen.
int connected;
@@ -316,7 +330,8 @@ PosixErrorOr<T> BindIP(int fd, bool dual_stack) {
template <typename T>
PosixErrorOr<T> TCPBindAndListen(int fd, bool dual_stack) {
ASSIGN_OR_RETURN_ERRNO(T addr, BindIP<T>(fd, dual_stack));
- RETURN_ERROR_IF_SYSCALL_FAIL(listen(fd, /* backlog = */ 5));
+ RETURN_ERROR_IF_SYSCALL_FAIL(
+ listen(fd, /* backlog = */ 5)); // NOLINT(bugprone-argument-comment)
return addr;
}
@@ -946,5 +961,169 @@ uint16_t ICMPChecksum(struct icmphdr icmphdr, const char* payload,
return csum;
}
+PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr) {
+ switch (family) {
+ case AF_INET:
+ return static_cast<uint16_t>(
+ reinterpret_cast<sockaddr_in const*>(&addr)->sin_port);
+ case AF_INET6:
+ return static_cast<uint16_t>(
+ reinterpret_cast<sockaddr_in6 const*>(&addr)->sin6_port);
+ default:
+ return PosixError(EINVAL,
+ absl::StrCat("unknown socket family: ", family));
+ }
+}
+
+PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port) {
+ switch (family) {
+ case AF_INET:
+ reinterpret_cast<sockaddr_in*>(addr)->sin_port = port;
+ return NoError();
+ case AF_INET6:
+ reinterpret_cast<sockaddr_in6*>(addr)->sin6_port = port;
+ return NoError();
+ default:
+ return PosixError(EINVAL,
+ absl::StrCat("unknown socket family: ", family));
+ }
+}
+
+void SetupTimeWaitClose(const TestAddress* listener,
+ const TestAddress* connector, bool reuse,
+ bool accept_close, sockaddr_storage* listen_addr,
+ sockaddr_storage* conn_bound_addr) {
+ // Create the listening socket.
+ FileDescriptor listen_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(listener->family(), SOCK_STREAM, IPPROTO_TCP));
+ if (reuse) {
+ ASSERT_THAT(setsockopt(listen_fd.get(), SOL_SOCKET, SO_REUSEADDR,
+ &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+ }
+ ASSERT_THAT(
+ bind(listen_fd.get(), AsSockAddr(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(), AsSockAddr(listen_addr), &addrlen),
+ SyscallSucceeds());
+
+ uint16_t const port =
+ ASSERT_NO_ERRNO_AND_VALUE(AddrPort(listener->family(), *listen_addr));
+
+ // Connect to the listening socket.
+ FileDescriptor conn_fd = ASSERT_NO_ERRNO_AND_VALUE(
+ Socket(connector->family(), SOCK_STREAM, IPPROTO_TCP));
+
+ // We disable saves after this point as a S/R causes the netstack seed
+ // to be regenerated which changes what ports/ISN is picked for a given
+ // tuple (src ip,src port, dst ip, dst port). This can cause the final
+ // SYN to use a sequence number that looks like one from the current
+ // connection in TIME_WAIT and will not be accepted causing the test
+ // to timeout.
+ //
+ // TODO(gvisor.dev/issue/940): S/R portSeed/portHint
+ DisableSave ds;
+
+ sockaddr_storage conn_addr = connector->addr;
+ ASSERT_NO_ERRNO(SetAddrPort(connector->family(), &conn_addr, port));
+ ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), AsSockAddr(&conn_addr),
+ connector->addr_len),
+ SyscallSucceeds());
+
+ // Accept the connection.
+ auto accepted =
+ ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_fd.get(), nullptr, nullptr));
+
+ // Get the address/port bound by the connecting socket.
+ socklen_t conn_addrlen = connector->addr_len;
+ ASSERT_THAT(
+ getsockname(conn_fd.get(), AsSockAddr(conn_bound_addr), &conn_addrlen),
+ SyscallSucceeds());
+
+ FileDescriptor active_closefd, passive_closefd;
+ if (accept_close) {
+ active_closefd = std::move(accepted);
+ passive_closefd = std::move(conn_fd);
+ } else {
+ active_closefd = std::move(conn_fd);
+ passive_closefd = std::move(accepted);
+ }
+
+ // shutdown to trigger TIME_WAIT.
+ ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds());
+ {
+ constexpr int kTimeout = 10000;
+ pollfd pfd = {
+ .fd = passive_closefd.get(),
+ .events = POLLIN,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ ASSERT_EQ(pfd.revents, POLLIN);
+ }
+ ASSERT_THAT(shutdown(passive_closefd.get(), SHUT_WR), SyscallSucceeds());
+ {
+ constexpr int kTimeout = 10000;
+ constexpr int16_t want_events = POLLHUP;
+ pollfd pfd = {
+ .fd = active_closefd.get(),
+ .events = want_events,
+ };
+ ASSERT_THAT(poll(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1));
+ }
+
+ // This sleep is needed to reduce flake to ensure that the passive-close
+ // ensures the state transitions to CLOSE from LAST_ACK.
+ absl::SleepFor(absl::Seconds(1));
+}
+
+constexpr char kRangeFile[] = "/proc/sys/net/ipv4/ip_local_port_range";
+
+PosixErrorOr<int> MaybeLimitEphemeralPorts() {
+ int min = 0;
+ int max = 1 << 16;
+
+ // Read the ephemeral range from /proc.
+ ASSIGN_OR_RETURN_ERRNO(std::string rangefile, GetContents(kRangeFile));
+ const std::string err_msg =
+ absl::StrFormat("%s has invalid content: %s", kRangeFile, rangefile);
+ if (rangefile.back() != '\n') {
+ return PosixError(EINVAL, err_msg);
+ }
+ rangefile.pop_back();
+ std::vector<std::string> range =
+ absl::StrSplit(rangefile, absl::ByAnyChar("\t "));
+ if (range.size() < 2 || !absl::SimpleAtoi(range.front(), &min) ||
+ !absl::SimpleAtoi(range.back(), &max)) {
+ return PosixError(EINVAL, err_msg);
+ }
+
+ // If we can open as writable, limit the range.
+ if (!access(kRangeFile, W_OK)) {
+ ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd,
+ Open(kRangeFile, O_WRONLY | O_TRUNC, 0));
+ int newMax = min + 50;
+ const std::string small_range = absl::StrFormat("%d %d", min, newMax);
+ int n = write(fd.get(), small_range.c_str(), small_range.size());
+ if (n < 0) {
+ // Hostinet doesn't allow modifying the host port range. And if we're root
+ // (as we are in some tests), access and open will succeed even if the
+ // file mode is readonly.
+ if (errno != EACCES) {
+ return PosixError(
+ errno,
+ absl::StrFormat("write(%d [%s], \"%s\", %d)", fd.get(), kRangeFile,
+ small_range.c_str(), small_range.size()));
+ }
+ } else {
+ max = newMax;
+ }
+ }
+ return max - min;
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_test_util.h b/test/util/socket_util.h
index 76dc090e0..0e2be63cc 100644
--- a/test/syscalls/linux/socket_test_util.h
+++ b/test/util/socket_util.h
@@ -564,6 +564,22 @@ inline sockaddr* AsSockAddr(sockaddr_un* s) {
return reinterpret_cast<sockaddr*>(s);
}
+PosixErrorOr<uint16_t> AddrPort(int family, sockaddr_storage const& addr);
+
+PosixError SetAddrPort(int family, sockaddr_storage* addr, uint16_t port);
+
+// setupTimeWaitClose sets up a socket endpoint in TIME_WAIT state.
+// Callers can choose to perform active close on either ends of the connection
+// and also specify if they want to enabled SO_REUSEADDR.
+void SetupTimeWaitClose(const TestAddress* listener,
+ const TestAddress* connector, bool reuse,
+ bool accept_close, sockaddr_storage* listen_addr,
+ sockaddr_storage* conn_bound_addr);
+
+// MaybeLimitEphemeralPorts attempts to reduce the number of ephemeral ports and
+// returns the number of ephemeral ports.
+PosixErrorOr<int> MaybeLimitEphemeralPorts();
+
namespace internal {
PosixErrorOr<int> TryPortAvailable(int port, AddressFamily family,
SocketType type, bool reuse_addr);
diff --git a/test/syscalls/linux/socket_test_util_impl.cc b/test/util/socket_util_impl.cc
index ef661a0e3..04550ad7c 100644
--- a/test/syscalls/linux/socket_test_util_impl.cc
+++ b/test/util/socket_util_impl.cc
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/socket_util.h"
namespace gvisor {
namespace testing {
diff --git a/test/util/test_util_impl.cc b/test/util/test_util_impl.cc
index 7e1ad9e66..6b6826898 100644
--- a/test/util/test_util_impl.cc
+++ b/test/util/test_util_impl.cc
@@ -20,6 +20,7 @@
#include "benchmark/benchmark.h"
#include "test/util/logging.h"
+extern bool FLAGS_gtest_list_tests;
extern bool FLAGS_benchmark_list_tests;
extern std::string FLAGS_benchmark_filter;
@@ -40,12 +41,18 @@ void TestInit(int* argc, char*** argv) {
}
int RunAllTests() {
- if (FLAGS_benchmark_list_tests || FLAGS_benchmark_filter != ".") {
+ if (::testing::FLAGS_gtest_list_tests) {
+ return RUN_ALL_TESTS();
+ }
+ if (FLAGS_benchmark_list_tests) {
benchmark::RunSpecifiedBenchmarks();
return 0;
- } else {
- return RUN_ALL_TESTS();
}
+
+ // Run selected tests & benchmarks.
+ int rc = RUN_ALL_TESTS();
+ benchmark::RunSpecifiedBenchmarks();
+ return rc;
}
} // namespace testing
diff --git a/test/util/verity_util.cc b/test/util/verity_util.cc
index f1b4c251b..b7d1cb212 100644
--- a/test/util/verity_util.cc
+++ b/test/util/verity_util.cc
@@ -54,18 +54,21 @@ PosixError FlipRandomBit(int fd, int size) {
return NoError();
}
-PosixErrorOr<std::string> MountVerity(std::string tmpfs_dir,
- std::string filename) {
- // Mount a verity fs on the existing tmpfs mount.
- std::string mount_opts = "lower_path=" + tmpfs_dir;
+PosixErrorOr<std::string> MountVerity(std::string lower_dir,
+ std::vector<EnableTarget> targets) {
+ // Mount a verity fs on the existing mount.
+ std::string mount_opts = "lower_path=" + lower_dir;
ASSIGN_OR_RETURN_ERRNO(TempPath verity_dir, TempPath::CreateDir());
RETURN_ERROR_IF_SYSCALL_FAIL(
mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str()));
- // Enable both the file and the directory.
- ASSIGN_OR_RETURN_ERRNO(
- auto fd, Open(JoinPath(verity_dir.path(), filename), O_RDONLY, 0777));
- RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(fd.get(), FS_IOC_ENABLE_VERITY));
+ for (const EnableTarget& target : targets) {
+ ASSIGN_OR_RETURN_ERRNO(
+ auto target_fd,
+ Open(JoinPath(verity_dir.path(), target.path), target.flags, 0777));
+ RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(target_fd.get(), FS_IOC_ENABLE_VERITY));
+ }
+
ASSIGN_OR_RETURN_ERRNO(auto dir_fd, Open(verity_dir.path(), O_RDONLY, 0777));
RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(dir_fd.get(), FS_IOC_ENABLE_VERITY));
@@ -83,6 +86,7 @@ PosixErrorOr<std::string> MountVerity(std::string tmpfs_dir,
ASSIGN_OR_RETURN_ERRNO(TempPath verity_with_hash_dir, TempPath::CreateDir());
RETURN_ERROR_IF_SYSCALL_FAIL(mount("", verity_with_hash_dir.path().c_str(),
"verity", 0, mount_opts.c_str()));
+
// Verity directories should not be deleted. Release the TempPath objects to
// prevent those directories from being deleted by the destructor.
verity_dir.release();
diff --git a/test/util/verity_util.h b/test/util/verity_util.h
index 18743ecd6..ebb78b4bb 100644
--- a/test/util/verity_util.h
+++ b/test/util/verity_util.h
@@ -17,6 +17,8 @@
#include <stdint.h>
+#include <vector>
+
#include "test/util/posix_error.h"
namespace gvisor {
@@ -44,6 +46,13 @@ struct fsverity_digest {
unsigned char digest[];
};
+struct EnableTarget {
+ std::string path;
+ int flags;
+
+ EnableTarget(std::string path, int flags) : path(path), flags(flags) {}
+};
+
constexpr int kMaxDigestSize = 64;
constexpr int kDefaultDigestSize = 32;
constexpr char kContents[] = "foobarbaz";
@@ -67,7 +76,7 @@ PosixError FlipRandomBit(int fd, int size);
// Mount a verity on the tmpfs and enable both the file and the direcotry. Then
// mount a new verity with measured root hash.
PosixErrorOr<std::string> MountVerity(std::string tmpfs_dir,
- std::string filename);
+ std::vector<EnableTarget> targets);
} // namespace testing
} // namespace gvisor