diff --git a/pkg/sentry/devices/nvproxy/frontend_mmap.go b/pkg/sentry/devices/nvproxy/frontend_mmap.go index 068d1c83b..c3e538477 100644 --- a/pkg/sentry/devices/nvproxy/frontend_mmap.go +++ b/pkg/sentry/devices/nvproxy/frontend_mmap.go @@ -72,6 +72,11 @@ func (mf *frontendFDMemmapFile) IncRef(fr memmap.FileRange, memCgID uint32) { func (mf *frontendFDMemmapFile) DecRef(fr memmap.FileRange) { } +// DataFD implements memmap.File.DataFD. +func (mf *frontendFDMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *frontendFDMemmapFile) FD() int { return int(mf.fd.hostFD) diff --git a/pkg/sentry/devices/nvproxy/uvm_mmap.go b/pkg/sentry/devices/nvproxy/uvm_mmap.go index a056879ad..eb9f8f7b3 100644 --- a/pkg/sentry/devices/nvproxy/uvm_mmap.go +++ b/pkg/sentry/devices/nvproxy/uvm_mmap.go @@ -85,6 +85,11 @@ func (mf *uvmFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessTy return safemem.BlockSeq{}, memmap.BufferedIOFallbackErr{} } +// DataFD implements memmap.File.DataFD. +func (mf *uvmFDMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *uvmFDMemmapFile) FD() int { return int(mf.fd.hostFD) diff --git a/pkg/sentry/devices/tpuproxy/accel/accel_fd_mmap.go b/pkg/sentry/devices/tpuproxy/accel/accel_fd_mmap.go index 4ae5060a1..1e11c7ba8 100644 --- a/pkg/sentry/devices/tpuproxy/accel/accel_fd_mmap.go +++ b/pkg/sentry/devices/tpuproxy/accel/accel_fd_mmap.go @@ -80,6 +80,11 @@ func (mf *accelFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.Access return safemem.BlockSeq{}, linuxerr.EINVAL } +// DataFD implements memmap.File.DataFD. +func (mf *accelFDMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *accelFDMemmapFile) FD() int { return int(mf.fd.hostFD) diff --git a/pkg/sentry/devices/tpuproxy/vfio/pci_device_fd_mmap.go b/pkg/sentry/devices/tpuproxy/vfio/pci_device_fd_mmap.go index a377d6fce..d8e640db9 100644 --- a/pkg/sentry/devices/tpuproxy/vfio/pci_device_fd_mmap.go +++ b/pkg/sentry/devices/tpuproxy/vfio/pci_device_fd_mmap.go @@ -91,6 +91,11 @@ func (mf *pciDeviceFdMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.Ac return mf.pfm.MapInternal(fr, int(mf.fd.hostFD), at.Write) } +// DataFD implements memmap.File.DataFD. +func (mf *pciDeviceFdMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *pciDeviceFdMemmapFile) FD() int { return int(mf.fd.hostFD) diff --git a/pkg/sentry/devices/tpuproxy/vfio/tpu_fd_mmap.go b/pkg/sentry/devices/tpuproxy/vfio/tpu_fd_mmap.go index 4a5ae3c9d..494d6859b 100644 --- a/pkg/sentry/devices/tpuproxy/vfio/tpu_fd_mmap.go +++ b/pkg/sentry/devices/tpuproxy/vfio/tpu_fd_mmap.go @@ -80,6 +80,11 @@ func (mf *tpuFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessTy return safemem.BlockSeq{}, linuxerr.EINVAL } +// DataFD implements memmap.File.DataFD. +func (mf *tpuFDMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *tpuFDMemmapFile) FD() int { return int(mf.fd.hostFD) diff --git a/pkg/sentry/devices/tpuproxy/vfio/vfio_fd_mmap.go b/pkg/sentry/devices/tpuproxy/vfio/vfio_fd_mmap.go index c7d555f26..b57a535db 100644 --- a/pkg/sentry/devices/tpuproxy/vfio/vfio_fd_mmap.go +++ b/pkg/sentry/devices/tpuproxy/vfio/vfio_fd_mmap.go @@ -80,6 +80,11 @@ func (mf *vfioFDMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessT return safemem.BlockSeq{}, linuxerr.EINVAL } +// DataFD implements memmap.File.DataFD. +func (mf *vfioFDMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *vfioFDMemmapFile) FD() int { return int(mf.fd.hostFD) diff --git a/pkg/sentry/fsimpl/erofs/regular_file.go b/pkg/sentry/fsimpl/erofs/regular_file.go index da758eb1c..b2f6ea76f 100644 --- a/pkg/sentry/fsimpl/erofs/regular_file.go +++ b/pkg/sentry/fsimpl/erofs/regular_file.go @@ -213,6 +213,11 @@ func (mf *imageMemmapFile) MapInternal(fr memmap.FileRange, at hostarch.AccessTy return safemem.BlockSeqOf(safemem.BlockFromSafeSlice(bytes)), nil } +// DataFD implements memmap.File.DataFD. +func (mf *imageMemmapFile) DataFD(fr memmap.FileRange) (int, error) { + return mf.FD(), nil +} + // FD implements memmap.File.FD. func (mf *imageMemmapFile) FD() int { return mf.image.FD() diff --git a/pkg/sentry/fsimpl/gofer/regular_file.go b/pkg/sentry/fsimpl/gofer/regular_file.go index 71754099b..8f029cb9d 100644 --- a/pkg/sentry/fsimpl/gofer/regular_file.go +++ b/pkg/sentry/fsimpl/gofer/regular_file.go @@ -960,6 +960,11 @@ func (d *dentryPlatformFile) MapInternal(fr memmap.FileRange, at hostarch.Access return d.hostFileMapper.MapInternal(fr, int(d.mmapFD.RacyLoad()), at.Write) } +// DataFD implements memmap.File.DataFD. +func (d *dentryPlatformFile) DataFD(fr memmap.FileRange) (int, error) { + return d.FD(), nil +} + // FD implements memmap.File.FD. func (d *dentryPlatformFile) FD() int { d.handleMu.RLock() diff --git a/pkg/sentry/fsimpl/gofer/special_file.go b/pkg/sentry/fsimpl/gofer/special_file.go index 5936bd508..93f9ed811 100644 --- a/pkg/sentry/fsimpl/gofer/special_file.go +++ b/pkg/sentry/fsimpl/gofer/special_file.go @@ -505,6 +505,11 @@ func (fd *specialFileFD) MapInternal(fr memmap.FileRange, at hostarch.AccessType return fd.hostFileMapper.MapInternal(fr, int(fd.handle.fd), at.Write) } +// DataFD implements memmap.File.DataFD. +func (fd *specialFileFD) DataFD(fr memmap.FileRange) (int, error) { + return fd.FD(), nil +} + // FD implements memmap.File.FD. func (fd *specialFileFD) FD() int { fd.requireHostFD() diff --git a/pkg/sentry/fsimpl/kernfs/mmap_util.go b/pkg/sentry/fsimpl/kernfs/mmap_util.go index 0f07a9406..85ca66bf0 100644 --- a/pkg/sentry/fsimpl/kernfs/mmap_util.go +++ b/pkg/sentry/fsimpl/kernfs/mmap_util.go @@ -72,6 +72,11 @@ func (i *inodePlatformFile) MapInternal(fr memmap.FileRange, at hostarch.AccessT return i.fileMapper.MapInternal(fr, i.hostFD, at.Write) } +// DataFD implements memmap.File.DataFD. +func (i *inodePlatformFile) DataFD(fr memmap.FileRange) (int, error) { + return i.FD(), nil +} + // FD implements memmap.File.FD. func (i *inodePlatformFile) FD() int { return i.hostFD diff --git a/pkg/sentry/fsimpl/tmpfs/regular_file.go b/pkg/sentry/fsimpl/tmpfs/regular_file.go index b524820a9..24c46fbaf 100644 --- a/pkg/sentry/fsimpl/tmpfs/regular_file.go +++ b/pkg/sentry/fsimpl/tmpfs/regular_file.go @@ -819,11 +819,15 @@ func (rw *regularFileReadWriter) writeToMF(fr memmap.FileRange, srcs safemem.Blo // causes a lot of context switching. Use write(2) host syscall instead, // which makes one context switch and faults all the pages that are touched // during the write. + fd, err := rw.file.inode.fs.mf.DataFD(fr) + if err != nil { + return 0, err + } return hostfd.Pwritev2( - int32(rw.file.inode.fs.mf.FD()), // fd - srcs.TakeFirst64(fr.Length()), // srcs - int64(fr.Start), // offset - 0, // flags + int32(fd), // fd + srcs.TakeFirst64(fr.Length()), // srcs + int64(fr.Start), // offset + 0, // flags ) } // Get internal mappings. diff --git a/pkg/sentry/memmap/memmap.go b/pkg/sentry/memmap/memmap.go index 091419d6b..90303bd24 100644 --- a/pkg/sentry/memmap/memmap.go +++ b/pkg/sentry/memmap/memmap.go @@ -440,6 +440,16 @@ type File interface { // reference is held on the mapped pages. MapInternal(fr FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) + // DataFD blocks until offsets fr in the file contain valid data, then + // returns the file descriptor represented by the File. + // + // Note that fr.Start and fr.End need not be page-aligned. + // + // Preconditions: + // * fr.Length() > 0. + // * At least one reference must be held on all pages in fr. + DataFD(fr FileRange) (int, error) + // BufferReadAt reads len(dst) bytes from the file into dst, starting at // file offset off. It returns the number of bytes read. Like // io.ReaderAt.ReadAt(), it never returns a short read with a nil error. @@ -466,10 +476,10 @@ type File interface { // * At least one reference must be held on all written pages. BufferWriteAt(off uint64, src []byte) (uint64, error) - // FD returns the file descriptor represented by the File. - // - // The only permitted operation on the returned file descriptor is to map - // pages from it consistent with the requirements of AddressSpace.MapFile. + // FD returns the file descriptor represented by the File. The returned + // file descriptor should not be used to implement + // platform.AddressSpace.MapFile, since the contents of the File may not be + // valid; use DataFD instead. FD() int } diff --git a/pkg/sentry/pgalloc/pgalloc.go b/pkg/sentry/pgalloc/pgalloc.go index 919f661c6..c6bb96e84 100644 --- a/pkg/sentry/pgalloc/pgalloc.go +++ b/pkg/sentry/pgalloc/pgalloc.go @@ -1751,6 +1751,11 @@ func (f *MemoryFile) File() *os.File { return f.file } +// DataFD implements memmap.File.DataFD. +func (f *MemoryFile) DataFD(fr memmap.FileRange) (int, error) { + return f.FD(), nil +} + // FD implements memmap.File.FD. func (f *MemoryFile) FD() int { return int(f.file.Fd()) diff --git a/pkg/sentry/platform/ptrace/subprocess.go b/pkg/sentry/platform/ptrace/subprocess.go index 8f8364c2e..d0f1df2c6 100644 --- a/pkg/sentry/platform/ptrace/subprocess.go +++ b/pkg/sentry/platform/ptrace/subprocess.go @@ -635,17 +635,21 @@ func (s *subprocess) syscall(sysno uintptr, args ...arch.SyscallArgument) (uintp // MapFile implements platform.AddressSpace.MapFile. func (s *subprocess) MapFile(addr hostarch.Addr, f memmap.File, fr memmap.FileRange, at hostarch.AccessType, precommit bool) error { + fd, err := f.DataFD(fr) + if err != nil { + return err + } var flags int if precommit { flags |= unix.MAP_POPULATE } - _, err := s.syscall( + _, err = s.syscall( unix.SYS_MMAP, arch.SyscallArgument{Value: uintptr(addr)}, arch.SyscallArgument{Value: uintptr(fr.Length())}, arch.SyscallArgument{Value: uintptr(at.Prot())}, arch.SyscallArgument{Value: uintptr(flags | unix.MAP_SHARED | unix.MAP_FIXED)}, - arch.SyscallArgument{Value: uintptr(f.FD())}, + arch.SyscallArgument{Value: uintptr(fd)}, arch.SyscallArgument{Value: uintptr(fr.Start)}) return err } diff --git a/pkg/sentry/platform/systrap/subprocess.go b/pkg/sentry/platform/systrap/subprocess.go index 11f08edba..ae77afd6a 100644 --- a/pkg/sentry/platform/systrap/subprocess.go +++ b/pkg/sentry/platform/systrap/subprocess.go @@ -149,8 +149,8 @@ type subprocess struct { // within the sentry address space. threadContextRegion uintptr - // memoryFile is used to allocate a sysmsg stack which is shared - // between a stub process and the Sentry. + // memoryFile is used to allocate a sysmsg stack which is shared between a + // stub process and the Sentry. memoryFile *pgalloc.MemoryFile // usertrap is the state of the usertrap table which contains syscall @@ -967,17 +967,21 @@ func (s *subprocess) syscall(sysno uintptr, args ...arch.SyscallArgument) (uintp // MapFile implements platform.AddressSpace.MapFile. func (s *subprocess) MapFile(addr hostarch.Addr, f memmap.File, fr memmap.FileRange, at hostarch.AccessType, precommit bool) error { + fd, err := f.DataFD(fr) + if err != nil { + return err + } var flags int if precommit { flags |= unix.MAP_POPULATE } - _, err := s.syscall( + _, err = s.syscall( unix.SYS_MMAP, arch.SyscallArgument{Value: uintptr(addr)}, arch.SyscallArgument{Value: uintptr(fr.Length())}, arch.SyscallArgument{Value: uintptr(at.Prot())}, arch.SyscallArgument{Value: uintptr(flags | unix.MAP_SHARED | unix.MAP_FIXED)}, - arch.SyscallArgument{Value: uintptr(f.FD())}, + arch.SyscallArgument{Value: uintptr(fd)}, arch.SyscallArgument{Value: uintptr(fr.Start)}) return err } diff --git a/pkg/sentry/platform/systrap/systrap.go b/pkg/sentry/platform/systrap/systrap.go index b573fb112..70cc19155 100644 --- a/pkg/sentry/platform/systrap/systrap.go +++ b/pkg/sentry/platform/systrap/systrap.go @@ -304,8 +304,9 @@ type Systrap struct { platform.UseHostGlobalMemoryBarrier platform.DoesNotOwnPageTables - // memoryFile is used to create a stub sysmsg stack - // which is shared with the Sentry. + // memoryFile is used to create a stub sysmsg stack which is shared with + // the Sentry. Since memoryFile is platform-private, it is never restored, + // so it is safe to call memoryFile.FD() rather than memoryFile.DataFD(). memoryFile *pgalloc.MemoryFile }