diff --git a/pkg/sentry/fdimport/BUILD b/pkg/sentry/fdimport/BUILD index 9b37f79ce..9eb35c545 100644 --- a/pkg/sentry/fdimport/BUILD +++ b/pkg/sentry/fdimport/BUILD @@ -18,5 +18,6 @@ go_library( "//pkg/sentry/kernel", "//pkg/sentry/kernel/auth", "//pkg/sentry/vfs", + "@org_golang_x_sys//unix:go_default_library", ], ) diff --git a/pkg/sentry/fdimport/fdimport.go b/pkg/sentry/fdimport/fdimport.go index cbe4d2491..c3a36af63 100644 --- a/pkg/sentry/fdimport/fdimport.go +++ b/pkg/sentry/fdimport/fdimport.go @@ -18,6 +18,7 @@ package fdimport import ( "fmt" + "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/fd" "gvisor.dev/gvisor/pkg/sentry/fsimpl/host" @@ -30,27 +31,43 @@ import ( // sets up TTY for sentry stdin, stdout, and stderr FDs. Used FDs are either // closed or released. It's safe for the caller to close any remaining files // upon return. -func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth.KUID, gid auth.KGID, stdioFDs map[int]*fd.FD) (*host.TTYFileDescription, error) { +func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth.KUID, gid auth.KGID, fds map[int]*fd.FD) (*host.TTYFileDescription, error) { k := kernel.KernelFromContext(ctx) if k == nil { return nil, fmt.Errorf("cannot find kernel from context") } + mnt := k.HostMount() + + // Collect host fds and flags. Do this before importing any fds because + // multiple fds may refer to the same file, and importing fds may + // change flags. + fdFlags := make(map[*fd.FD]kernel.FDFlags) + for _, hostFD := range fds { + var err error + fdFlags[hostFD], err = getFDFlags(hostFD) + if err != nil { + return nil, err + } + } var ttyFile *vfs.FileDescription - for appFD, hostFD := range stdioFDs { + for appFD, hostFD := range fds { + fdOpts := host.NewFDOptions{ + Savable: true, + } + if uid != auth.NoID || gid != auth.NoID { + fdOpts.VirtualOwner = true + fdOpts.UID = uid + fdOpts.GID = gid + } var appFile *vfs.FileDescription if console && appFD < 3 { // Import the file as a host TTY file. if ttyFile == nil { + fdOpts.IsTTY = true var err error - appFile, err = host.NewFD(ctx, k.HostMount(), hostFD.FD(), &host.NewFDOptions{ - Savable: true, - IsTTY: true, - VirtualOwner: true, - UID: uid, - GID: gid, - }) + appFile, err = host.NewFD(ctx, mnt, hostFD.FD(), &fdOpts) if err != nil { return nil, err } @@ -68,12 +85,7 @@ func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth } } else { var err error - appFile, err = host.NewFD(ctx, k.HostMount(), hostFD.FD(), &host.NewFDOptions{ - Savable: true, - VirtualOwner: true, - UID: uid, - GID: gid, - }) + appFile, err = host.NewFD(ctx, mnt, hostFD.FD(), &fdOpts) if err != nil { return nil, err } @@ -81,13 +93,22 @@ func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, uid auth hostFD.Release() // FD is transfered to host FD. } - if err := fdTable.NewFDAt(ctx, int32(appFD), appFile, kernel.FDFlags{}); err != nil { + if err := fdTable.NewFDAt(ctx, int32(appFD), appFile, fdFlags[hostFD]); err != nil { return nil, err } } - if ttyFile == nil { return nil, nil } return ttyFile.Impl().(*host.TTYFileDescription), nil } + +func getFDFlags(f *fd.FD) (kernel.FDFlags, error) { + fdflags, _, errno := unix.Syscall(unix.SYS_FCNTL, uintptr(f.FD()), unix.F_GETFD, 0) + if errno != 0 { + return kernel.FDFlags{}, fmt.Errorf("failed to get fd flags for fd %d (errno=%d)", f, errno) + } + return kernel.FDFlags{ + CloseOnExec: fdflags&unix.O_CLOEXEC != 0, + }, nil +}