summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDean Deng <deandeng@google.com>2020-03-26 16:46:15 -0700
committergVisor bot <gvisor-bot@google.com>2020-03-26 16:47:20 -0700
commit137f3614009b0ef931c1d00a083b4ae8e6a39bc9 (patch)
tree0411beb065a44e5a42e2e5d272d13d4aa5a190c0
parentedc3c049eb553fcbf32f4a6b515141a26c5609d4 (diff)
Use host-defined file owner and mode, when possible, for imported fds.
Using the host-defined file owner matches VFS1. It is more correct to use the host-defined mode, since the cached value may become out of date. However, kernfs.Inode.Mode() does not return an error--other filesystems on kernfs are in-memory so retrieving mode should not fail. Therefore, if the host syscall fails, we rely on a cached value instead. Updates #1672. PiperOrigin-RevId: 303220864
-rw-r--r--pkg/sentry/control/proc.go6
-rw-r--r--pkg/sentry/fs/host/BUILD1
-rw-r--r--pkg/sentry/fs/host/control.go2
-rw-r--r--pkg/sentry/fs/host/file.go10
-rw-r--r--pkg/sentry/fs/host/inode_test.go3
-rw-r--r--pkg/sentry/fs/host/wait_test.go3
-rw-r--r--pkg/sentry/fsimpl/host/host.go110
-rw-r--r--runsc/boot/fds.go5
8 files changed, 87 insertions, 53 deletions
diff --git a/pkg/sentry/control/proc.go b/pkg/sentry/control/proc.go
index 5457ba5e7..b51fb3959 100644
--- a/pkg/sentry/control/proc.go
+++ b/pkg/sentry/control/proc.go
@@ -224,8 +224,6 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
}
}
- mounter := fs.FileOwnerFromContext(ctx)
-
// TODO(gvisor.dev/issue/1623): Use host FD when supported in VFS2.
var ttyFile *fs.File
for appFD, hostFile := range args.FilePayload.Files {
@@ -235,7 +233,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
// Import the file as a host TTY file.
if ttyFile == nil {
var err error
- appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), mounter, true /* isTTY */)
+ appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), true /* isTTY */)
if err != nil {
return nil, 0, nil, err
}
@@ -254,7 +252,7 @@ func (proc *Proc) execAsync(args *ExecArgs) (*kernel.ThreadGroup, kernel.ThreadI
} else {
// Import the file as a regular host file.
var err error
- appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), mounter, false /* isTTY */)
+ appFile, err = host.ImportFile(ctx, int(hostFile.Fd()), false /* isTTY */)
if err != nil {
return nil, 0, nil, err
}
diff --git a/pkg/sentry/fs/host/BUILD b/pkg/sentry/fs/host/BUILD
index 011625c80..aabce6cc9 100644
--- a/pkg/sentry/fs/host/BUILD
+++ b/pkg/sentry/fs/host/BUILD
@@ -71,7 +71,6 @@ go_test(
"//pkg/fd",
"//pkg/fdnotifier",
"//pkg/sentry/contexttest",
- "//pkg/sentry/fs",
"//pkg/sentry/kernel/time",
"//pkg/sentry/socket",
"//pkg/sentry/socket/unix/transport",
diff --git a/pkg/sentry/fs/host/control.go b/pkg/sentry/fs/host/control.go
index cd84e1337..52c0504b6 100644
--- a/pkg/sentry/fs/host/control.go
+++ b/pkg/sentry/fs/host/control.go
@@ -78,7 +78,7 @@ func fdsToFiles(ctx context.Context, fds []int) []*fs.File {
}
// Create the file backed by hostFD.
- file, err := NewFile(ctx, fd, fs.FileOwnerFromContext(ctx))
+ file, err := NewFile(ctx, fd)
if err != nil {
ctx.Warningf("Error creating file from host FD: %v", err)
break
diff --git a/pkg/sentry/fs/host/file.go b/pkg/sentry/fs/host/file.go
index 034862694..3e48b8b2c 100644
--- a/pkg/sentry/fs/host/file.go
+++ b/pkg/sentry/fs/host/file.go
@@ -60,8 +60,8 @@ var _ fs.FileOperations = (*fileOperations)(nil)
// The returned File cannot be saved, since there is no guarantee that the same
// FD will exist or represent the same file at time of restore. If such a
// guarantee does exist, use ImportFile instead.
-func NewFile(ctx context.Context, fd int, mounter fs.FileOwner) (*fs.File, error) {
- return newFileFromDonatedFD(ctx, fd, mounter, false, false)
+func NewFile(ctx context.Context, fd int) (*fs.File, error) {
+ return newFileFromDonatedFD(ctx, fd, false, false)
}
// ImportFile creates a new File backed by the provided host file descriptor.
@@ -71,13 +71,13 @@ func NewFile(ctx context.Context, fd int, mounter fs.FileOwner) (*fs.File, error
// If the returned file is saved, it will be restored by re-importing the FD
// originally passed to ImportFile. It is the restorer's responsibility to
// ensure that the FD represents the same file.
-func ImportFile(ctx context.Context, fd int, mounter fs.FileOwner, isTTY bool) (*fs.File, error) {
- return newFileFromDonatedFD(ctx, fd, mounter, true, isTTY)
+func ImportFile(ctx context.Context, fd int, isTTY bool) (*fs.File, error) {
+ return newFileFromDonatedFD(ctx, fd, true, isTTY)
}
// newFileFromDonatedFD returns an fs.File from a donated FD. If the FD is
// saveable, then saveable is true.
-func newFileFromDonatedFD(ctx context.Context, donated int, mounter fs.FileOwner, saveable, isTTY bool) (*fs.File, error) {
+func newFileFromDonatedFD(ctx context.Context, donated int, saveable, isTTY bool) (*fs.File, error) {
var s syscall.Stat_t
if err := syscall.Fstat(donated, &s); err != nil {
return nil, err
diff --git a/pkg/sentry/fs/host/inode_test.go b/pkg/sentry/fs/host/inode_test.go
index 4c374681c..c507f57eb 100644
--- a/pkg/sentry/fs/host/inode_test.go
+++ b/pkg/sentry/fs/host/inode_test.go
@@ -19,7 +19,6 @@ import (
"testing"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs"
)
// TestCloseFD verifies fds will be closed.
@@ -33,7 +32,7 @@ func TestCloseFD(t *testing.T) {
// Use the write-end because we will detect if it's closed on the read end.
ctx := contexttest.Context(t)
- file, err := NewFile(ctx, p[1], fs.RootOwner)
+ file, err := NewFile(ctx, p[1])
if err != nil {
t.Fatalf("Failed to create File: %v", err)
}
diff --git a/pkg/sentry/fs/host/wait_test.go b/pkg/sentry/fs/host/wait_test.go
index d49c3a635..ce397a5e3 100644
--- a/pkg/sentry/fs/host/wait_test.go
+++ b/pkg/sentry/fs/host/wait_test.go
@@ -20,7 +20,6 @@ import (
"time"
"gvisor.dev/gvisor/pkg/sentry/contexttest"
- "gvisor.dev/gvisor/pkg/sentry/fs"
"gvisor.dev/gvisor/pkg/waiter"
)
@@ -34,7 +33,7 @@ func TestWait(t *testing.T) {
defer syscall.Close(fds[1])
ctx := contexttest.Context(t)
- file, err := NewFile(ctx, fds[0], fs.RootOwner)
+ file, err := NewFile(ctx, fds[0])
if err != nil {
syscall.Close(fds[0])
t.Fatalf("NewFile failed: %v", err)
diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go
index a54985ef5..17e3d6e9d 100644
--- a/pkg/sentry/fsimpl/host/host.go
+++ b/pkg/sentry/fsimpl/host/host.go
@@ -54,7 +54,7 @@ func NewMount(vfsObj *vfs.VirtualFilesystem) (*vfs.Mount, error) {
}
// ImportFD sets up and returns a vfs.FileDescription from a donated fd.
-func ImportFD(mnt *vfs.Mount, hostFD int, ownerUID auth.KUID, ownerGID auth.KGID, isTTY bool) (*vfs.FileDescription, error) {
+func ImportFD(mnt *vfs.Mount, hostFD int, isTTY bool) (*vfs.FileDescription, error) {
fs, ok := mnt.Filesystem().Impl().(*kernfs.Filesystem)
if !ok {
return nil, fmt.Errorf("can't import host FDs into filesystems of type %T", mnt.Filesystem().Impl())
@@ -78,8 +78,6 @@ func ImportFD(mnt *vfs.Mount, hostFD int, ownerUID auth.KUID, ownerGID auth.KGID
canMap: canMap(uint32(fileType)),
ino: fs.NextIno(),
mode: fileMode,
- uid: ownerUID,
- gid: ownerGID,
// For simplicity, set offset to 0. Technically, we should
// only set to 0 on files that are not seekable (sockets, pipes, etc.),
// and use the offset from the host fd otherwise.
@@ -135,17 +133,20 @@ type inode struct {
// This field is initialized at creation time and is immutable.
ino uint64
- // TODO(gvisor.dev/issue/1672): protect mode, uid, and gid with mutex.
+ // modeMu protects mode.
+ modeMu sync.Mutex
- // mode is the file mode of this inode. Note that this value may become out
- // of date if the mode is changed on the host, e.g. with chmod.
+ // mode is a cached version of the file mode on the host. Note that it may
+ // become out of date if the mode is changed on the host, e.g. with chmod.
+ //
+ // Generally, it is better to retrieve the mode from the host through an
+ // fstat syscall. We only use this value in inode.Mode(), which cannot
+ // return an error, if the syscall to host fails.
+ //
+ // FIXME(b/152294168): Plumb error into Inode.Mode() return value so we
+ // can get rid of this.
mode linux.FileMode
- // uid and gid of the file owner. Note that these refer to the owner of the
- // file created on import, not the fd on the host.
- uid auth.KUID
- gid auth.KGID
-
// offsetMu protects offset.
offsetMu sync.Mutex
@@ -168,12 +169,35 @@ func fileFlagsFromHostFD(fd int) (int, error) {
// CheckPermissions implements kernfs.Inode.
func (i *inode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
- return vfs.GenericCheckPermissions(creds, ats, i.mode, i.uid, i.gid)
+ mode, uid, gid, err := i.getPermissions()
+ if err != nil {
+ return err
+ }
+ return vfs.GenericCheckPermissions(creds, ats, mode, uid, gid)
}
// Mode implements kernfs.Inode.
func (i *inode) Mode() linux.FileMode {
- return i.mode
+ mode, _, _, err := i.getPermissions()
+ if err != nil {
+ return i.mode
+ }
+
+ return linux.FileMode(mode)
+}
+
+func (i *inode) getPermissions() (linux.FileMode, auth.KUID, auth.KGID, error) {
+ // Retrieve metadata.
+ var s syscall.Stat_t
+ if err := syscall.Fstat(i.hostFD, &s); err != nil {
+ return 0, 0, 0, err
+ }
+
+ // Update cached mode.
+ i.modeMu.Lock()
+ i.mode = linux.FileMode(s.Mode)
+ i.modeMu.Unlock()
+ return linux.FileMode(s.Mode), auth.KUID(s.Uid), auth.KGID(s.Gid), nil
}
// Stat implements kernfs.Inode.
@@ -213,45 +237,51 @@ func (i *inode) Stat(_ *vfs.Filesystem, opts vfs.StatOptions) (linux.Statx, erro
ls.Attributes = s.Attributes
ls.AttributesMask = s.Attributes_mask
- if mask|linux.STATX_TYPE != 0 {
+ if mask&linux.STATX_TYPE != 0 {
ls.Mode |= s.Mode & linux.S_IFMT
}
- if mask|linux.STATX_MODE != 0 {
+ if mask&linux.STATX_MODE != 0 {
ls.Mode |= s.Mode &^ linux.S_IFMT
}
- if mask|linux.STATX_NLINK != 0 {
+ if mask&linux.STATX_NLINK != 0 {
ls.Nlink = s.Nlink
}
- if mask|linux.STATX_ATIME != 0 {
+ if mask&linux.STATX_UID != 0 {
+ ls.UID = s.Uid
+ }
+ if mask&linux.STATX_GID != 0 {
+ ls.GID = s.Gid
+ }
+ if mask&linux.STATX_ATIME != 0 {
ls.Atime = unixToLinuxStatxTimestamp(s.Atime)
}
- if mask|linux.STATX_BTIME != 0 {
+ if mask&linux.STATX_BTIME != 0 {
ls.Btime = unixToLinuxStatxTimestamp(s.Btime)
}
- if mask|linux.STATX_CTIME != 0 {
+ if mask&linux.STATX_CTIME != 0 {
ls.Ctime = unixToLinuxStatxTimestamp(s.Ctime)
}
- if mask|linux.STATX_MTIME != 0 {
+ if mask&linux.STATX_MTIME != 0 {
ls.Mtime = unixToLinuxStatxTimestamp(s.Mtime)
}
- if mask|linux.STATX_SIZE != 0 {
+ if mask&linux.STATX_SIZE != 0 {
ls.Size = s.Size
}
- if mask|linux.STATX_BLOCKS != 0 {
+ if mask&linux.STATX_BLOCKS != 0 {
ls.Blocks = s.Blocks
}
- // Use our own internal inode number and file owner.
- if mask|linux.STATX_INO != 0 {
+ // Use our own internal inode number.
+ if mask&linux.STATX_INO != 0 {
ls.Ino = i.ino
}
- if mask|linux.STATX_UID != 0 {
- ls.UID = uint32(i.uid)
- }
- if mask|linux.STATX_GID != 0 {
- ls.GID = uint32(i.gid)
- }
+ // Update cached mode.
+ if (mask&linux.STATX_TYPE != 0) && (mask&linux.STATX_MODE != 0) {
+ i.modeMu.Lock()
+ i.mode = linux.FileMode(s.Mode)
+ i.modeMu.Unlock()
+ }
return ls, nil
}
@@ -274,6 +304,8 @@ func (i *inode) fstat(opts vfs.StatOptions) (linux.Statx, error) {
Mask: linux.STATX_BASIC_STATS,
Blksize: uint32(s.Blksize),
Nlink: uint32(s.Nlink),
+ UID: s.Uid,
+ GID: s.Gid,
Mode: uint16(s.Mode),
Size: uint64(s.Size),
Blocks: uint64(s.Blocks),
@@ -282,15 +314,13 @@ func (i *inode) fstat(opts vfs.StatOptions) (linux.Statx, error) {
Mtime: timespecToStatxTimestamp(s.Mtim),
}
- // Use our own internal inode number and file owner.
+ // Use our own internal inode number.
//
// TODO(gvisor.dev/issue/1672): Use a kernfs-specific device number as well.
// If we use the device number from the host, it may collide with another
// sentry-internal device number. We handle device/inode numbers without
// relying on the host to prevent collisions.
ls.Ino = i.ino
- ls.UID = uint32(i.uid)
- ls.GID = uint32(i.gid)
return ls, nil
}
@@ -306,7 +336,11 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
if m&^(linux.STATX_MODE|linux.STATX_SIZE|linux.STATX_ATIME|linux.STATX_MTIME) != 0 {
return syserror.EPERM
}
- if err := vfs.CheckSetStat(ctx, creds, &s, i.Mode(), i.uid, i.gid); err != nil {
+ mode, uid, gid, err := i.getPermissions()
+ if err != nil {
+ return err
+ }
+ if err := vfs.CheckSetStat(ctx, creds, &s, mode.Permissions(), uid, gid); err != nil {
return err
}
@@ -314,7 +348,9 @@ func (i *inode) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Cre
if err := syscall.Fchmod(i.hostFD, uint32(s.Mode)); err != nil {
return err
}
+ i.modeMu.Lock()
i.mode = linux.FileMode(s.Mode)
+ i.modeMu.Unlock()
}
if m&linux.STATX_SIZE != 0 {
if err := syscall.Ftruncate(i.hostFD, int64(s.Size)); err != nil {
@@ -351,7 +387,11 @@ func (i *inode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptio
}
func (i *inode) open(d *vfs.Dentry, mnt *vfs.Mount) (*vfs.FileDescription, error) {
- fileType := i.mode.FileType()
+ mode, _, _, err := i.getPermissions()
+ if err != nil {
+ return nil, err
+ }
+ fileType := mode.FileType()
if fileType == syscall.S_IFSOCK {
if i.isTTY {
return nil, errors.New("cannot use host socket as TTY")
diff --git a/runsc/boot/fds.go b/runsc/boot/fds.go
index 417d2d5fb..5314b0f2a 100644
--- a/runsc/boot/fds.go
+++ b/runsc/boot/fds.go
@@ -34,7 +34,6 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []int) (*kernel.F
k := kernel.KernelFromContext(ctx)
fdTable := k.NewFDTable()
defer fdTable.DecRef()
- mounter := fs.FileOwnerFromContext(ctx)
var ttyFile *fs.File
for appFD, hostFD := range stdioFDs {
@@ -44,7 +43,7 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []int) (*kernel.F
// Import the file as a host TTY file.
if ttyFile == nil {
var err error
- appFile, err = host.ImportFile(ctx, hostFD, mounter, true /* isTTY */)
+ appFile, err = host.ImportFile(ctx, hostFD, true /* isTTY */)
if err != nil {
return nil, err
}
@@ -63,7 +62,7 @@ func createFDTable(ctx context.Context, console bool, stdioFDs []int) (*kernel.F
} else {
// Import the file as a regular host file.
var err error
- appFile, err = host.ImportFile(ctx, hostFD, mounter, false /* isTTY */)
+ appFile, err = host.ImportFile(ctx, hostFD, false /* isTTY */)
if err != nil {
return nil, err
}