diff --git a/runsc/fsgofer/lisafs.go b/runsc/fsgofer/lisafs.go index dca243cc1..265b7b9f4 100644 --- a/runsc/fsgofer/lisafs.go +++ b/runsc/fsgofer/lisafs.go @@ -116,7 +116,7 @@ func (s *LisafsServer) Mount(c *lisafs.Connection, mountNode *lisafs.Node) (*lis return nil, linux.Statx{}, -1, err } - if err := checkSupportedFileType(uint32(stat.Mode), &s.config); err != nil { + if err := checkSupportedFileType(uint32(stat.Mode)); err != nil { log.Warningf("Mount: checkSupportedFileType() failed for file %q with mode %o: %v", mountPath, stat.Mode, err) return nil, linux.Statx{}, -1, err } @@ -408,7 +408,7 @@ func (fd *controlFDLisa) Walk(name string) (*lisafs.ControlFD, linux.Statx, erro return nil, linux.Statx{}, err } - if err := checkSupportedFileType(uint32(stat.Mode), &fd.Conn().ServerImpl().(*LisafsServer).config); err != nil { + if err := checkSupportedFileType(uint32(stat.Mode)); err != nil { _ = unix.Close(childHostFD) log.Warningf("Walk: checkSupportedFileType() failed for %q with mode %o: %v", name, stat.Mode, err) return nil, linux.Statx{}, err @@ -445,7 +445,6 @@ func (fd *controlFDLisa) WalkStat(path lisafs.StringArray, recordStat func(linux if fd.IsSymlink() { return nil } - server := fd.Conn().ServerImpl().(*LisafsServer) for _, name := range path { curFD, err := unix.Openat(curDirFD, name, unix.O_PATH|openFlags, 0) if err == unix.ENOENT { @@ -463,7 +462,7 @@ func (fd *controlFDLisa) WalkStat(path lisafs.StringArray, recordStat func(linux if err != nil { return err } - if err := checkSupportedFileType(uint32(stat.Mode), &server.config); err != nil { + if err := checkSupportedFileType(uint32(stat.Mode)); err != nil { log.Warningf("WalkStat: checkSupportedFileType() failed for file %q with mode %o while walking path %+v: %v", name, stat.Mode, path, err) return err } @@ -481,6 +480,18 @@ func (fd *controlFDLisa) WalkStat(path lisafs.StringArray, recordStat func(linux // Open implements lisafs.ControlFDImpl.Open. func (fd *controlFDLisa) Open(flags uint32) (*lisafs.OpenFD, int, error) { + ftype := fd.FileType() + server := fd.Conn().ServerImpl().(*LisafsServer) + switch ftype { + case unix.S_IFIFO: + if !server.config.HostFifo.AllowOpen() { + return nil, -1, unix.EPERM + } + case unix.S_IFSOCK: + if !server.config.HostUDS.AllowOpen() { + return nil, -1, unix.EPERM + } + } flags |= openFlags openHostFD, err := unix.Openat(int(procSelfFD.FD()), strconv.Itoa(fd.hostFD), int(flags)&^unix.O_NOFOLLOW, 0) if err != nil { @@ -488,7 +499,6 @@ func (fd *controlFDLisa) Open(flags uint32) (*lisafs.OpenFD, int, error) { } hostFDToDonate := -1 - ftype := fd.FileType() switch { case ftype == unix.S_IFREG: // Best effort to donate file to the Sentry (for performance only). @@ -1140,21 +1150,9 @@ func fstatTo(hostFD int) (linux.Statx, error) { }, nil } -func checkSupportedFileType(mode uint32, config *Config) error { +func checkSupportedFileType(mode uint32) error { switch mode & unix.S_IFMT { - case unix.S_IFREG, unix.S_IFDIR, unix.S_IFLNK, unix.S_IFCHR: - return nil - - case unix.S_IFSOCK: - if !config.HostUDS.AllowOpen() { - return unix.EPERM - } - return nil - - case unix.S_IFIFO: - if !config.HostFifo.AllowOpen() { - return unix.EPERM - } + case unix.S_IFREG, unix.S_IFDIR, unix.S_IFLNK, unix.S_IFCHR, unix.S_IFSOCK, unix.S_IFIFO: return nil default: diff --git a/test/e2e/BUILD b/test/e2e/BUILD index 21a0df049..9ce631302 100644 --- a/test/e2e/BUILD +++ b/test/e2e/BUILD @@ -26,6 +26,7 @@ go_test( "//pkg/test/testutil", "//runsc/specutils", "@com_github_docker_docker//api/types/mount:go_default_library", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go index 504c6ba59..9b9ad7e62 100644 --- a/test/e2e/integration_test.go +++ b/test/e2e/integration_test.go @@ -39,6 +39,7 @@ import ( "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" ) @@ -997,3 +998,51 @@ func TestCharDevice(t *testing.T) { t.Errorf("Wrong bytes, want: [all zeros], got: %v", got) } } + +func TestBlockHostUds(t *testing.T) { + ctx := context.Background() + d := dockerutil.MakeContainer(ctx, t) + defer d.CleanUp(ctx) + + dir, err := os.MkdirTemp(testutil.TmpDir(), "tmp-mount") + if err != nil { + t.Fatalf("MkdirTemp() failed: %v", err) + } + defer os.RemoveAll(dir) + + dirFD, err := unix.Open(dir, unix.O_PATH, 0) + if err != nil { + t.Fatalf("failed to open %s: %v", dir, err) + } + defer unix.Close(dirFD) + // 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(dirFD), "test.sock")) + if err != nil { + t.Fatalf("listen error: %v", err) + } + defer l.Close() + + opts := dockerutil.RunOpts{ + Image: "basic/integrationtest", + WorkDir: "/root", + Mounts: []mount.Mount{ + { + Type: mount.TypeBind, + Source: dir, + Target: "/dir", + }, + }, + } + if err := d.Spawn(ctx, opts, "sleep", "infinity"); err != nil { + t.Fatalf("docker run failed: %v", err) + } + // Application should be able to walk/stat the UDS ... + if got, err := d.Exec(ctx, dockerutil.ExecOpts{}, "stat", "/dir/test.sock"); err != nil { + t.Fatalf("stat(2)-ing the UDS failed: output = %q, err = %v", got, err) + } + // ... but not connect to it. + const want = "connect: Connection refused" + if got, err := d.Exec(ctx, dockerutil.ExecOpts{}, "./host_connect", "/dir/test.sock"); err == nil || !strings.Contains(got, want) { + t.Errorf("err should be non-nil and output should contain %q, but got err = %v and output = %q", want, err, got) + } +}