diff --git a/pkg/sentry/fsimpl/eventfd/eventfd.go b/pkg/sentry/fsimpl/eventfd/eventfd.go index 07c16874ba..3b012c0e37 100644 --- a/pkg/sentry/fsimpl/eventfd/eventfd.go +++ b/pkg/sentry/fsimpl/eventfd/eventfd.go @@ -82,6 +82,25 @@ func New(ctx context.Context, vfsObj *vfs.VirtualFilesystem, initVal uint64, sem return &efd.vfsfd, nil } +// NewFromHost creates a new event fd from the given host fd. +func NewFromHost(ctx context.Context, vfsObj *vfs.VirtualFilesystem, hostfd int, flags uint32) (*vfs.FileDescription, error) { + semMode := flags&linux.EFD_SEMAPHORE != 0 + fileFlags := uint32(linux.O_RDWR) + if flags&linux.EFD_NONBLOCK != 0 { + fileFlags |= linux.O_NONBLOCK + } + fd, err := New(ctx, vfsObj, 0, semMode, flags) + if err != nil { + return nil, err + } + efd := fd.Impl().(*EventFileDescription) + efd.hostfd = hostfd + if err := fdnotifier.AddFD(int32(hostfd), &efd.queue); err != nil { + return nil, err + } + return fd, nil +} + // HostFD returns the host eventfd associated with this event. func (efd *EventFileDescription) HostFD() (int, error) { efd.mu.Lock() diff --git a/pkg/sentry/fsimpl/host/BUILD b/pkg/sentry/fsimpl/host/BUILD index 460c15adb6..22819ffc25 100644 --- a/pkg/sentry/fsimpl/host/BUILD +++ b/pkg/sentry/fsimpl/host/BUILD @@ -42,6 +42,7 @@ go_library( "//pkg/refs", "//pkg/safemem", "//pkg/sentry/arch", + "//pkg/sentry/fsimpl/eventfd", "//pkg/sentry/fsimpl/kernfs", "//pkg/sentry/fsimpl/lock", "//pkg/sentry/fsutil", diff --git a/pkg/sentry/fsimpl/host/host.go b/pkg/sentry/fsimpl/host/host.go index 2aa0a74916..b79f712986 100644 --- a/pkg/sentry/fsimpl/host/host.go +++ b/pkg/sentry/fsimpl/host/host.go @@ -30,6 +30,7 @@ import ( "gvisor.dev/gvisor/pkg/hostarch" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/arch" + "gvisor.dev/gvisor/pkg/sentry/fsimpl/eventfd" "gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs" "gvisor.dev/gvisor/pkg/sentry/hostfd" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" @@ -281,6 +282,11 @@ func NewFD(ctx context.Context, mnt *vfs.Mount, hostFD int, opts *NewFDOptions) } fileType := linux.FileMode(stat.Mode).FileType() + if fileType == 0 && isHostEventFdDevice(stat.Dev) { + // This is an event fd. No inode needed. + vfsObj := mnt.Filesystem().VirtualFilesystem() + return eventfd.NewFromHost(ctx, vfsObj, hostFD, flags) + } i, err := newInode(ctx, fs, hostFD, opts.Savable, opts.RestoreKey, fileType, opts.IsTTY, opts.Readonly) if err != nil { return nil, err @@ -1027,3 +1033,28 @@ func (f *fileDescription) Ioctl(ctx context.Context, uio usermem.IO, sysno uintp return f.FileDescriptionDefaultImpl.Ioctl(ctx, uio, sysno, args) } + +// hostEventFdDevice is the host device that host event fds are associated +// with. It is calculated once lazily. +var hostEventFdDevice uint64 +var hostEventFdDeviceOnce sync.Once + +// isHostEventFdDevice initializes hostEventFdDevice and compares it to the +// given host device id. +func isHostEventFdDevice(dev uint64) bool { + hostEventFdDeviceOnce.Do(func() { + efd, _, err := unix.RawSyscall(unix.SYS_EVENTFD2, 0, 0, 0) + if err != 0 { + log.Warningf("failed to create dummy eventfd: %v. Importing eventfds will fail", error(err)) + return + } + defer unix.Close(int(efd)) + var stat unix.Stat_t + if err := unix.Fstat(int(efd), &stat); err != nil { + log.Warningf("failed to stat dummy eventfd: %v. Importing eventfds will fail", error(err)) + return + } + hostEventFdDevice = stat.Dev + }) + return dev == hostEventFdDevice +}