diff --git a/Makefile b/Makefile index 1583d5ae8..23f8d13c5 100644 --- a/Makefile +++ b/Makefile @@ -268,9 +268,9 @@ INTEGRATION_TARGETS := //test/image:image_test //test/e2e:integration_test docker-tests: load-basic $(RUNTIME_BIN) @$(call install_runtime,$(RUNTIME),) # Clear flags. - # Used by TestRlimitNoFile. - @$(call install_runtime,$(RUNTIME)-fdlimit,--fdlimit=2000) - @$(call install_runtime,$(RUNTIME)-dcache,--fdlimit=2000 --dcache=100) + @$(call install_runtime,$(RUNTIME)-fdlimit,--fdlimit=2000) # Used by TestRlimitNoFile. + @$(call install_runtime,$(RUNTIME)-dcache,--fdlimit=2000 --dcache=100) # Used by TestDentryCacheLimit. + @$(call install_runtime,$(RUNTIME)-host-uds,--host-uds=all) # Used by TestHostSocketConnect. @$(call test_runtime,$(RUNTIME),$(INTEGRATION_TARGETS) //test/e2e:integration_runtime_test) .PHONY: docker-tests diff --git a/images/basic/integrationtest/Dockerfile b/images/basic/integrationtest/Dockerfile index 0a022f3d4..8b237bf65 100644 --- a/images/basic/integrationtest/Dockerfile +++ b/images/basic/integrationtest/Dockerfile @@ -11,4 +11,5 @@ RUN gcc -O2 -o test_copy_up test_copy_up.c RUN gcc -O2 -o test_rewinddir test_rewinddir.c RUN gcc -O2 -o link_test link_test.c RUN gcc -O2 -o test_sticky test_sticky.c -RUN gcc -O2 -o host_fd host_fd.c \ No newline at end of file +RUN gcc -O2 -o host_fd host_fd.c +RUN gcc -O2 -o host_connect host_connect.c \ No newline at end of file diff --git a/images/basic/integrationtest/host_connect.c b/images/basic/integrationtest/host_connect.c new file mode 100644 index 000000000..e4f6a3a75 --- /dev/null +++ b/images/basic/integrationtest/host_connect.c @@ -0,0 +1,47 @@ +// Copyright 2022 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + int fd; + struct sockaddr_un addr; + char buff[10]; + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + perror("socket"); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, argv[1]); + if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + perror("connect"); + exit(1); + } + + strcpy(buff, "Hello"); + if (send(fd, buff, strlen(buff) + 1, 0) == -1) { + perror("send"); + exit(1); + } + + close(fd); + exit(0); +} diff --git a/test/e2e/BUILD b/test/e2e/BUILD index d9f71f838..53c6219e0 100644 --- a/test/e2e/BUILD +++ b/test/e2e/BUILD @@ -43,6 +43,7 @@ go_test( "//pkg/test/dockerutil", "//pkg/test/testutil", "@com_github_docker_docker//api/types/mount:go_default_library", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/test/e2e/integration_runtime_test.go b/test/e2e/integration_runtime_test.go index f25184941..34e74562e 100644 --- a/test/e2e/integration_runtime_test.go +++ b/test/e2e/integration_runtime_test.go @@ -25,12 +25,17 @@ import ( "context" "flag" "io/ioutil" + "net" "os" + "path/filepath" + "strconv" "strings" + "sync" "testing" "time" "github.com/docker/docker/api/types/mount" + "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/test/dockerutil" "gvisor.dev/gvisor/pkg/test/testutil" ) @@ -115,3 +120,61 @@ func TestDentryCacheLimit(t *testing.T) { t.Fatalf("docker failed: %v, %s", err, got) } } + +// NOTE(gvisor.dev/issue/8126): Regression test. +func TestHostSocketConnect(t *testing.T) { + ctx := context.Background() + d := dockerutil.MakeContainerWithRuntime(ctx, t, "-host-uds") + defer d.CleanUp(ctx) + + tmpDir := testutil.TmpDir() + tmpDirFD, err := unix.Open(tmpDir, unix.O_PATH, 0) + if err != nil { + t.Fatalf("open error: %v", err) + } + defer unix.Close(tmpDirFD) + // Use /proc/self/fd to generate path to avoid EINVAL on large path. + l, err := net.Listen("unix", filepath.Join("/proc/self/fd", strconv.Itoa(tmpDirFD), "test.sock")) + if err != nil { + t.Fatalf("listen error: %v", err) + } + defer l.Close() + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + conn, err := l.Accept() + if err != nil { + t.Errorf("accept error: %v", err) + return + } + + conn.SetReadDeadline(time.Now().Add(30 * time.Second)) + var buf [5]byte + if _, err := conn.Read(buf[:]); err != nil { + t.Errorf("read error: %v", err) + return + } + + if want := "Hello"; string(buf[:]) != want { + t.Errorf("expected %s, got %v", want, string(buf[:])) + } + }() + + opts := dockerutil.RunOpts{ + Image: "basic/integrationtest", + WorkDir: "/root", + Mounts: []mount.Mount{ + { + Type: mount.TypeBind, + Source: filepath.Join(tmpDir, "test.sock"), + Target: "/test.sock", + }, + }, + } + if _, err := d.Run(ctx, opts, "./host_connect", "/test.sock"); err != nil { + t.Fatalf("docker run failed: %v", err) + } + wg.Wait() +}