diff --git a/pkg/sentry/devices/memdev/full.go b/pkg/sentry/devices/memdev/full.go index fc702c9f6..d7cfa599c 100644 --- a/pkg/sentry/devices/memdev/full.go +++ b/pkg/sentry/devices/memdev/full.go @@ -47,6 +47,7 @@ type fullFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.SpliceInFD } // Release implements vfs.FileDescriptionImpl.Release. diff --git a/pkg/sentry/devices/memdev/null.go b/pkg/sentry/devices/memdev/null.go index ff5837747..a55c06633 100644 --- a/pkg/sentry/devices/memdev/null.go +++ b/pkg/sentry/devices/memdev/null.go @@ -48,6 +48,7 @@ type nullFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.SpliceInFD } // Release implements vfs.FileDescriptionImpl.Release. diff --git a/pkg/sentry/devices/memdev/random.go b/pkg/sentry/devices/memdev/random.go index ac943e3ba..0c7ef6aec 100644 --- a/pkg/sentry/devices/memdev/random.go +++ b/pkg/sentry/devices/memdev/random.go @@ -53,6 +53,7 @@ type randomFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.SpliceInFD // off is the "file offset". off is accessed using atomic memory // operations. diff --git a/pkg/sentry/devices/memdev/zero.go b/pkg/sentry/devices/memdev/zero.go index 49c53452a..cb46266c6 100644 --- a/pkg/sentry/devices/memdev/zero.go +++ b/pkg/sentry/devices/memdev/zero.go @@ -50,6 +50,7 @@ type zeroFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.SpliceInFD } // Release implements vfs.FileDescriptionImpl.Release. diff --git a/pkg/sentry/devices/tundev/tundev.go b/pkg/sentry/devices/tundev/tundev.go index c98c8d3f8..2fb2a029d 100644 --- a/pkg/sentry/devices/tundev/tundev.go +++ b/pkg/sentry/devices/tundev/tundev.go @@ -60,6 +60,7 @@ type tunFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.SpliceInFD device tun.Device } diff --git a/pkg/sentry/fsimpl/devpts/master.go b/pkg/sentry/fsimpl/devpts/master.go index 35a682b40..17ab4b122 100644 --- a/pkg/sentry/fsimpl/devpts/master.go +++ b/pkg/sentry/fsimpl/devpts/master.go @@ -90,6 +90,7 @@ type masterFileDescription struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.NoSpliceInFD inode *masterInode t *Terminal diff --git a/pkg/sentry/fsimpl/devpts/replica.go b/pkg/sentry/fsimpl/devpts/replica.go index eced9e618..5235d7cbe 100644 --- a/pkg/sentry/fsimpl/devpts/replica.go +++ b/pkg/sentry/fsimpl/devpts/replica.go @@ -102,6 +102,7 @@ type replicaFileDescription struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.NoSpliceInFD inode *replicaInode } diff --git a/pkg/sentry/fsimpl/eventfd/eventfd.go b/pkg/sentry/fsimpl/eventfd/eventfd.go index f79d2b252..49d93eb1d 100644 --- a/pkg/sentry/fsimpl/eventfd/eventfd.go +++ b/pkg/sentry/fsimpl/eventfd/eventfd.go @@ -42,6 +42,7 @@ type EventFileDescription struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.NoSpliceInFD // queue is used to notify interested parties when the event object // becomes readable or writable. diff --git a/pkg/sentry/fsimpl/fuse/dev.go b/pkg/sentry/fsimpl/fuse/dev.go index 55d86b933..77483e902 100644 --- a/pkg/sentry/fsimpl/fuse/dev.go +++ b/pkg/sentry/fsimpl/fuse/dev.go @@ -57,6 +57,7 @@ type DeviceFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.SpliceInFD // nextOpID is used to create new requests. nextOpID linux.FUSEOpID diff --git a/pkg/sentry/fsimpl/fuse/directory.go b/pkg/sentry/fsimpl/fuse/directory.go index 9611edd5a..880466434 100644 --- a/pkg/sentry/fsimpl/fuse/directory.go +++ b/pkg/sentry/fsimpl/fuse/directory.go @@ -28,6 +28,7 @@ import ( type directoryFD struct { fileDescription + vfs.NoSpliceInFD } // Allocate implements directoryFD.Allocate. diff --git a/pkg/sentry/fsimpl/fuse/regular_file.go b/pkg/sentry/fsimpl/fuse/regular_file.go index 38cde8208..c2270df5d 100644 --- a/pkg/sentry/fsimpl/fuse/regular_file.go +++ b/pkg/sentry/fsimpl/fuse/regular_file.go @@ -29,6 +29,7 @@ import ( type regularFileFD struct { fileDescription + vfs.SpliceInFD // off is the file offset. off int64 diff --git a/pkg/sentry/fsimpl/gofer/gofer.go b/pkg/sentry/fsimpl/gofer/gofer.go index 5ef3cf445..a6039d103 100644 --- a/pkg/sentry/fsimpl/gofer/gofer.go +++ b/pkg/sentry/fsimpl/gofer/gofer.go @@ -2443,6 +2443,7 @@ type fileDescription struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.SpliceInFD lockLogging sync.Once `state:"nosave"` } diff --git a/pkg/sentry/fsimpl/gofer/regular_file.go b/pkg/sentry/fsimpl/gofer/regular_file.go index 874f9873d..d48b4200f 100644 --- a/pkg/sentry/fsimpl/gofer/regular_file.go +++ b/pkg/sentry/fsimpl/gofer/regular_file.go @@ -45,6 +45,7 @@ func (d *dentry) isRegularFile() bool { // +stateify savable type regularFileFD struct { fileDescription + vfs.SpliceInFD // off is the file offset. off is protected by mu. mu sync.Mutex `state:"nosave"` diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index c4e58f82c..b469b35d1 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -649,6 +649,7 @@ type fileDescription struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.SpliceInFD // inode is vfsfd.Dentry().Impl().(*kernfs.Dentry).Inode().(*inode), but // cached to reduce indirections and casting. fileDescription does not hold diff --git a/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go b/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go index 652ade564..59871d880 100644 --- a/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go +++ b/pkg/sentry/fsimpl/kernfs/dynamic_bytes_file.go @@ -89,6 +89,7 @@ type DynamicBytesFD struct { vfs.FileDescriptionDefaultImpl vfs.DynamicBytesFileDescriptionImpl vfs.LockFD + vfs.NoSpliceInFD vfsfd vfs.FileDescription inode Inode diff --git a/pkg/sentry/fsimpl/mqfs/queue.go b/pkg/sentry/fsimpl/mqfs/queue.go index eee2bd8f6..8cfbdd9c4 100644 --- a/pkg/sentry/fsimpl/mqfs/queue.go +++ b/pkg/sentry/fsimpl/mqfs/queue.go @@ -61,6 +61,7 @@ type queueFD struct { vfs.FileDescriptionDefaultImpl vfs.DynamicBytesFileDescriptionImpl vfs.LockFD + vfs.NoSpliceInFD vfsfd vfs.FileDescription inode kernfs.Inode diff --git a/pkg/sentry/fsimpl/overlay/regular_file.go b/pkg/sentry/fsimpl/overlay/regular_file.go index 65d270c69..42ec73165 100644 --- a/pkg/sentry/fsimpl/overlay/regular_file.go +++ b/pkg/sentry/fsimpl/overlay/regular_file.go @@ -50,6 +50,7 @@ func (d *dentry) readlink(ctx context.Context) (string, error) { // +stateify savable type regularFileFD struct { fileDescription + vfs.SpliceInFD // If copiedUp is false, cachedFD represents // fileDescription.dentry().lowerVDs[0]; otherwise, cachedFD represents diff --git a/pkg/sentry/fsimpl/proc/task_files.go b/pkg/sentry/fsimpl/proc/task_files.go index d3f9cf489..9936a1e29 100644 --- a/pkg/sentry/fsimpl/proc/task_files.go +++ b/pkg/sentry/fsimpl/proc/task_files.go @@ -434,6 +434,7 @@ type memFD struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.SpliceInFD inode *memInode @@ -678,6 +679,7 @@ type statusFD struct { statusFDLowerBase vfs.DynamicBytesFileDescriptionImpl vfs.LockFD + vfs.NoSpliceInFD vfsfd vfs.FileDescription @@ -1173,6 +1175,7 @@ func (i *namespaceInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *ker type namespaceFD struct { vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.NoSpliceInFD vfsfd vfs.FileDescription inode *namespaceInode diff --git a/pkg/sentry/fsimpl/signalfd/signalfd.go b/pkg/sentry/fsimpl/signalfd/signalfd.go index a1f8aed14..129748acf 100644 --- a/pkg/sentry/fsimpl/signalfd/signalfd.go +++ b/pkg/sentry/fsimpl/signalfd/signalfd.go @@ -34,6 +34,7 @@ type SignalFileDescription struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.NoSpliceInFD // target is the original signal target task. // diff --git a/pkg/sentry/fsimpl/sys/kcov.go b/pkg/sentry/fsimpl/sys/kcov.go index 51f0bf3d8..06efa4cb5 100644 --- a/pkg/sentry/fsimpl/sys/kcov.go +++ b/pkg/sentry/fsimpl/sys/kcov.go @@ -67,6 +67,7 @@ func (i *kcovInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.D type kcovFD struct { vfs.FileDescriptionDefaultImpl vfs.NoLockFD + vfs.SpliceInFD vfsfd vfs.FileDescription inode *kcovInode diff --git a/pkg/sentry/fsimpl/timerfd/timerfd.go b/pkg/sentry/fsimpl/timerfd/timerfd.go index 467c76596..94f4e39ef 100644 --- a/pkg/sentry/fsimpl/timerfd/timerfd.go +++ b/pkg/sentry/fsimpl/timerfd/timerfd.go @@ -36,6 +36,7 @@ type TimerFileDescription struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.NoLockFD + vfs.NoSpliceInFD events waiter.Queue timer *ktime.Timer diff --git a/pkg/sentry/fsimpl/tmpfs/regular_file.go b/pkg/sentry/fsimpl/tmpfs/regular_file.go index 3a30e043f..439c57af8 100644 --- a/pkg/sentry/fsimpl/tmpfs/regular_file.go +++ b/pkg/sentry/fsimpl/tmpfs/regular_file.go @@ -332,6 +332,7 @@ func (*regularFile) InvalidateUnsavable(context.Context) error { // +stateify savable type regularFileFD struct { fileDescription + vfs.SpliceInFD // off is the file offset. off is accessed using atomic memory operations. // offMu serializes operations that may mutate off. diff --git a/pkg/sentry/fsimpl/verity/verity.go b/pkg/sentry/fsimpl/verity/verity.go index d2526263c..d144b8921 100644 --- a/pkg/sentry/fsimpl/verity/verity.go +++ b/pkg/sentry/fsimpl/verity/verity.go @@ -989,6 +989,7 @@ func (d *dentry) readlink(ctx context.Context) (string, error) { type fileDescription struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl + vfs.NoSpliceInFD // d is the corresponding dentry to the fileDescription. d *dentry diff --git a/pkg/sentry/kernel/pipe/vfs.go b/pkg/sentry/kernel/pipe/vfs.go index a3011bacf..bb6f8f84d 100644 --- a/pkg/sentry/kernel/pipe/vfs.go +++ b/pkg/sentry/kernel/pipe/vfs.go @@ -186,6 +186,7 @@ type VFSPipeFD struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.LockFD + vfs.SpliceInFD pipe *Pipe } diff --git a/pkg/sentry/socket/hostinet/socket_vfs2.go b/pkg/sentry/socket/hostinet/socket_vfs2.go index 5bea70106..d7bc899fe 100644 --- a/pkg/sentry/socket/hostinet/socket_vfs2.go +++ b/pkg/sentry/socket/hostinet/socket_vfs2.go @@ -36,6 +36,7 @@ type socketVFS2 struct { vfsfd vfs.FileDescription vfs.FileDescriptionDefaultImpl vfs.LockFD + vfs.SpliceInFD // We store metadata for hostinet sockets internally. Technically, we should // access metadata (e.g. through stat, chmod) on the host for correctness, diff --git a/pkg/sentry/socket/netlink/socket_vfs2.go b/pkg/sentry/socket/netlink/socket_vfs2.go index 0de568e5a..ee4dd93f2 100644 --- a/pkg/sentry/socket/netlink/socket_vfs2.go +++ b/pkg/sentry/socket/netlink/socket_vfs2.go @@ -43,6 +43,7 @@ type SocketVFS2 struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.LockFD + vfs.SpliceInFD socketOpsCommon } diff --git a/pkg/sentry/socket/netstack/netstack_vfs2.go b/pkg/sentry/socket/netstack/netstack_vfs2.go index 145951145..0cfc5bfcf 100644 --- a/pkg/sentry/socket/netstack/netstack_vfs2.go +++ b/pkg/sentry/socket/netstack/netstack_vfs2.go @@ -41,6 +41,7 @@ type SocketVFS2 struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.LockFD + vfs.SpliceInFD socketOpsCommon } diff --git a/pkg/sentry/socket/unix/unix_vfs2.go b/pkg/sentry/socket/unix/unix_vfs2.go index fd1194bc1..f110a0103 100644 --- a/pkg/sentry/socket/unix/unix_vfs2.go +++ b/pkg/sentry/socket/unix/unix_vfs2.go @@ -44,6 +44,7 @@ type SocketVFS2 struct { vfs.FileDescriptionDefaultImpl vfs.DentryMetadataFileDescriptionImpl vfs.LockFD + vfs.SpliceInFD socketVFS2Refs socketOpsCommon diff --git a/pkg/sentry/syscalls/linux/vfs2/splice.go b/pkg/sentry/syscalls/linux/vfs2/splice.go index d8580b44a..aee74c0e1 100644 --- a/pkg/sentry/syscalls/linux/vfs2/splice.go +++ b/pkg/sentry/syscalls/linux/vfs2/splice.go @@ -70,6 +70,9 @@ func Splice(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal if !inFile.IsReadable() || !outFile.IsWritable() { return 0, nil, linuxerr.EBADF } + if !outFile.Impl().SpliceInSupported() { + return 0, nil, linuxerr.EINVAL + } // The operation is non-blocking if anything is non-blocking. // @@ -213,6 +216,9 @@ func Tee(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallCo if !inFile.IsReadable() || !outFile.IsWritable() { return 0, nil, linuxerr.EBADF } + if !outFile.Impl().SpliceInSupported() { + return 0, nil, linuxerr.EINVAL + } // The operation is non-blocking if anything is non-blocking. // @@ -285,6 +291,9 @@ func Sendfile(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Sysc if !outFile.IsWritable() { return 0, nil, linuxerr.EBADF } + if !outFile.Impl().SpliceInSupported() { + return 0, nil, linuxerr.EINVAL + } // Verify that the outFile Append flag is not set. if outFile.StatusFlags()&linux.O_APPEND != 0 { diff --git a/pkg/sentry/vfs/epoll.go b/pkg/sentry/vfs/epoll.go index 128e743f3..e27872671 100644 --- a/pkg/sentry/vfs/epoll.go +++ b/pkg/sentry/vfs/epoll.go @@ -34,6 +34,7 @@ type EpollInstance struct { FileDescriptionDefaultImpl DentryMetadataFileDescriptionImpl NoLockFD + NoSpliceInFD // q holds waiters on this EpollInstance. q waiter.Queue diff --git a/pkg/sentry/vfs/file_description.go b/pkg/sentry/vfs/file_description.go index 76188b68b..d979d819e 100644 --- a/pkg/sentry/vfs/file_description.go +++ b/pkg/sentry/vfs/file_description.go @@ -473,6 +473,9 @@ type FileDescriptionImpl interface { // TestPOSIX returns information about whether the specified lock can be held, in the style of the F_GETLK fcntl. TestPOSIX(ctx context.Context, uid lock.UniqueID, t lock.LockType, r lock.LockRange) (linux.Flock, error) + + // SpliceInSupported returns true if splice into this descriptor is allowed. + SpliceInSupported() bool } // Dirent holds the information contained in struct linux_dirent64. diff --git a/pkg/sentry/vfs/file_description_impl_util.go b/pkg/sentry/vfs/file_description_impl_util.go index f8b35f248..6628ffaba 100644 --- a/pkg/sentry/vfs/file_description_impl_util.go +++ b/pkg/sentry/vfs/file_description_impl_util.go @@ -199,6 +199,11 @@ func (DirectoryFileDescriptionDefaultImpl) Write(ctx context.Context, src userme return 0, linuxerr.EISDIR } +// SpliceInSupported implements FileDescriptionImpl.SpliceInSupported. +func (DirectoryFileDescriptionDefaultImpl) SpliceInSupported() bool { + return false +} + // DentryMetadataFileDescriptionImpl may be embedded by implementations of // FileDescriptionImpl for which FileDescriptionOptions.UseDentryMetadata is // true to obtain implementations of Stat and SetStat that panic. @@ -530,3 +535,25 @@ func (BadLockFD) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, r fslock. func (BadLockFD) TestPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, r fslock.LockRange) (linux.Flock, error) { return linux.Flock{}, linuxerr.EBADF } + +// SpliceInFD implements SpliceInSupported of FileDescriptionImpl interface +// returning true. +// +// +stateify savable +type SpliceInFD struct{} + +// SpliceInSupported implements FileDescriptionImpl.SpliceInSupported. +func (SpliceInFD) SpliceInSupported() bool { + return true +} + +// NoSpliceInFD implements SpliceInSupported of FileDescriptionImpl interface +// returning false. +// +// +stateify savable +type NoSpliceInFD struct{} + +// SpliceInSupported implements FileDescriptionImpl.SpliceInSupported. +func (NoSpliceInFD) SpliceInSupported() bool { + return false +} diff --git a/pkg/sentry/vfs/file_description_impl_util_test.go b/pkg/sentry/vfs/file_description_impl_util_test.go index e34a8c11b..533925060 100644 --- a/pkg/sentry/vfs/file_description_impl_util_test.go +++ b/pkg/sentry/vfs/file_description_impl_util_test.go @@ -34,6 +34,7 @@ type fileDescription struct { vfsfd FileDescription FileDescriptionDefaultImpl NoLockFD + NoSpliceInFD } // genCount contains the number of times its DynamicBytesSource.Generate() diff --git a/pkg/sentry/vfs/inotify.go b/pkg/sentry/vfs/inotify.go index 9b5e63317..0165b5963 100644 --- a/pkg/sentry/vfs/inotify.go +++ b/pkg/sentry/vfs/inotify.go @@ -58,6 +58,7 @@ type Inotify struct { FileDescriptionDefaultImpl DentryMetadataFileDescriptionImpl NoLockFD + NoSpliceInFD // Unique identifier for this inotify instance. We don't just reuse the // inotify fd because fds can be duped. These should not be exposed to the diff --git a/pkg/sentry/vfs/opath.go b/pkg/sentry/vfs/opath.go index da0b33b79..461681fae 100644 --- a/pkg/sentry/vfs/opath.go +++ b/pkg/sentry/vfs/opath.go @@ -31,6 +31,7 @@ type opathFD struct { vfsfd FileDescription FileDescriptionDefaultImpl BadLockFD + NoSpliceInFD } // Release implements FileDescriptionImpl.Release.