mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Port memfd_create to vfs2 and finish implementation of file seals.
Closes #2612. PiperOrigin-RevId: 311548074
This commit is contained in:
committed by
gVisor bot
parent
f1ad2d54ab
commit
47dfba7661
@@ -52,7 +52,6 @@ go_library(
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/fsutil",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
"//pkg/sentry/kernel/time",
|
||||
@@ -106,7 +105,6 @@ go_test(
|
||||
"//pkg/sentry/contexttest",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/contexttest",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/syserror",
|
||||
"//pkg/usermem",
|
||||
|
||||
@@ -772,5 +772,24 @@ func (fs *filesystem) RemovexattrAt(ctx context.Context, rp *vfs.ResolvingPath,
|
||||
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
|
||||
fs.mu.RLock()
|
||||
defer fs.mu.RUnlock()
|
||||
return genericPrependPath(vfsroot, vd.Mount(), vd.Dentry().Impl().(*dentry), b)
|
||||
mnt := vd.Mount()
|
||||
d := vd.Dentry().Impl().(*dentry)
|
||||
for {
|
||||
if mnt == vfsroot.Mount() && &d.vfsd == vfsroot.Dentry() {
|
||||
return vfs.PrependPathAtVFSRootError{}
|
||||
}
|
||||
if &d.vfsd == mnt.Root() {
|
||||
return nil
|
||||
}
|
||||
if d.parent == nil {
|
||||
if d.name != "" {
|
||||
// This must be an anonymous memfd file.
|
||||
b.PrependComponent("/" + d.name)
|
||||
return vfs.PrependPathSyntheticError{}
|
||||
}
|
||||
return vfs.PrependPathAtNonMountRootError{}
|
||||
}
|
||||
b.PrependComponent(d.name)
|
||||
d = d.parent
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ type regularFile struct {
|
||||
func (fs *filesystem) newRegularFile(creds *auth.Credentials, mode linux.FileMode) *inode {
|
||||
file := ®ularFile{
|
||||
memFile: fs.memFile,
|
||||
seals: linux.F_SEAL_SEAL,
|
||||
}
|
||||
file.inode.init(file, fs, creds, linux.S_IFREG|mode)
|
||||
file.inode.nlink = 1 // from parent directory
|
||||
@@ -577,3 +578,44 @@ exitLoop:
|
||||
|
||||
return done, retErr
|
||||
}
|
||||
|
||||
// GetSeals returns the current set of seals on a memfd inode.
|
||||
func GetSeals(fd *vfs.FileDescription) (uint32, error) {
|
||||
f, ok := fd.Impl().(*regularFileFD)
|
||||
if !ok {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
rf := f.inode().impl.(*regularFile)
|
||||
rf.dataMu.RLock()
|
||||
defer rf.dataMu.RUnlock()
|
||||
return rf.seals, nil
|
||||
}
|
||||
|
||||
// AddSeals adds new file seals to a memfd inode.
|
||||
func AddSeals(fd *vfs.FileDescription, val uint32) error {
|
||||
f, ok := fd.Impl().(*regularFileFD)
|
||||
if !ok {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
rf := f.inode().impl.(*regularFile)
|
||||
rf.mapsMu.Lock()
|
||||
defer rf.mapsMu.Unlock()
|
||||
rf.dataMu.RLock()
|
||||
defer rf.dataMu.RUnlock()
|
||||
|
||||
if rf.seals&linux.F_SEAL_SEAL != 0 {
|
||||
// Seal applied which prevents addition of any new seals.
|
||||
return syserror.EPERM
|
||||
}
|
||||
|
||||
// F_SEAL_WRITE can only be added if there are no active writable maps.
|
||||
if rf.seals&linux.F_SEAL_WRITE == 0 && val&linux.F_SEAL_WRITE != 0 {
|
||||
if rf.writableMappingPages > 0 {
|
||||
return syserror.EBUSY
|
||||
}
|
||||
}
|
||||
|
||||
// Seals can only be added, never removed.
|
||||
rf.seals |= val
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/usermem"
|
||||
|
||||
@@ -19,8 +19,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/contexttest"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
)
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ type FilesystemOpts struct {
|
||||
}
|
||||
|
||||
// GetFilesystem implements vfs.FilesystemType.GetFilesystem.
|
||||
func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
|
||||
func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, _ string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
|
||||
memFileProvider := pgalloc.MemoryFileProviderFromContext(ctx)
|
||||
if memFileProvider == nil {
|
||||
panic("MemoryFileProviderFromContext returned nil")
|
||||
@@ -139,6 +139,11 @@ func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.Virt
|
||||
return &fs.vfsfs, &root.vfsd, nil
|
||||
}
|
||||
|
||||
// NewFilesystem returns a new tmpfs filesystem.
|
||||
func NewFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials) (*vfs.Filesystem, *vfs.Dentry, error) {
|
||||
return FilesystemType{}.GetFilesystem(ctx, vfsObj, creds, "", vfs.GetFilesystemOptions{})
|
||||
}
|
||||
|
||||
// Release implements vfs.FilesystemImpl.Release.
|
||||
func (fs *filesystem) Release() {
|
||||
fs.vfsfs.VirtualFilesystem().PutAnonBlockDevMinor(fs.devMinor)
|
||||
@@ -658,3 +663,34 @@ func (fd *fileDescription) Setxattr(ctx context.Context, opts vfs.SetxattrOption
|
||||
func (fd *fileDescription) Removexattr(ctx context.Context, name string) error {
|
||||
return fd.inode().removexattr(auth.CredentialsFromContext(ctx), name)
|
||||
}
|
||||
|
||||
// NewMemfd creates a new tmpfs regular file and file description that can back
|
||||
// an anonymous fd created by memfd_create.
|
||||
func NewMemfd(mount *vfs.Mount, creds *auth.Credentials, allowSeals bool, name string) (*vfs.FileDescription, error) {
|
||||
fs, ok := mount.Filesystem().Impl().(*filesystem)
|
||||
if !ok {
|
||||
panic("NewMemfd() called with non-tmpfs mount")
|
||||
}
|
||||
|
||||
// Per Linux, mm/shmem.c:__shmem_file_setup(), memfd inodes are set up with
|
||||
// S_IRWXUGO.
|
||||
mode := linux.FileMode(0777)
|
||||
inode := fs.newRegularFile(creds, mode)
|
||||
rf := inode.impl.(*regularFile)
|
||||
if allowSeals {
|
||||
rf.seals = 0
|
||||
}
|
||||
|
||||
d := fs.newDentry(inode)
|
||||
defer d.DecRef()
|
||||
d.name = name
|
||||
|
||||
// Per Linux, mm/shmem.c:__shmem_file_setup(), memfd files are set up with
|
||||
// FMODE_READ | FMODE_WRITE.
|
||||
var fd regularFileFD
|
||||
flags := uint32(linux.O_RDWR)
|
||||
if err := fd.vfsfd.Init(&fd, flags, mount, &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &fd.vfsfd, nil
|
||||
}
|
||||
|
||||
@@ -173,6 +173,7 @@ go_library(
|
||||
"//pkg/sentry/fsimpl/pipefs",
|
||||
"//pkg/sentry/fsimpl/sockfs",
|
||||
"//pkg/sentry/fsimpl/timerfd",
|
||||
"//pkg/sentry/fsimpl/tmpfs",
|
||||
"//pkg/sentry/hostcpu",
|
||||
"//pkg/sentry/inet",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
|
||||
@@ -53,6 +53,7 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/pipefs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/sockfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/timerfd"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/hostcpu"
|
||||
"gvisor.dev/gvisor/pkg/sentry/inet"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -259,6 +260,10 @@ type Kernel struct {
|
||||
// syscalls (as opposed to named pipes created by mknod()).
|
||||
pipeMount *vfs.Mount
|
||||
|
||||
// shmMount is the Mount used for anonymous files created by the
|
||||
// memfd_create() syscalls. It is analagous to Linux's shm_mnt.
|
||||
shmMount *vfs.Mount
|
||||
|
||||
// socketMount is the Mount used for sockets created by the socket() and
|
||||
// socketpair() syscalls. There are several cases where a socket dentry will
|
||||
// not be contained in socketMount:
|
||||
@@ -330,6 +335,9 @@ func (k *Kernel) Init(args InitKernelArgs) error {
|
||||
if args.Timekeeper == nil {
|
||||
return fmt.Errorf("Timekeeper is nil")
|
||||
}
|
||||
if args.Timekeeper.clocks == nil {
|
||||
return fmt.Errorf("Must call Timekeeper.SetClocks() before Kernel.Init()")
|
||||
}
|
||||
if args.RootUserNamespace == nil {
|
||||
return fmt.Errorf("RootUserNamespace is nil")
|
||||
}
|
||||
@@ -384,6 +392,18 @@ func (k *Kernel) Init(args InitKernelArgs) error {
|
||||
}
|
||||
k.pipeMount = pipeMount
|
||||
|
||||
tmpfsFilesystem, tmpfsRoot, err := tmpfs.NewFilesystem(k.SupervisorContext(), &k.vfs, auth.NewRootCredentials(k.rootUserNamespace))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tmpfs filesystem: %v", err)
|
||||
}
|
||||
defer tmpfsFilesystem.DecRef()
|
||||
defer tmpfsRoot.DecRef()
|
||||
shmMount, err := k.vfs.NewDisconnectedMount(tmpfsFilesystem, tmpfsRoot, &vfs.MountOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create tmpfs mount: %v", err)
|
||||
}
|
||||
k.shmMount = shmMount
|
||||
|
||||
socketFilesystem, err := sockfs.NewFilesystem(&k.vfs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create sockfs filesystem: %v", err)
|
||||
@@ -1656,6 +1676,11 @@ func (k *Kernel) PipeMount() *vfs.Mount {
|
||||
return k.pipeMount
|
||||
}
|
||||
|
||||
// ShmMount returns the tmpfs mount.
|
||||
func (k *Kernel) ShmMount() *vfs.Mount {
|
||||
return k.shmMount
|
||||
}
|
||||
|
||||
// SocketMount returns the sockfs mount.
|
||||
func (k *Kernel) SocketMount() *vfs.Mount {
|
||||
return k.socketMount
|
||||
|
||||
@@ -13,6 +13,7 @@ go_library(
|
||||
"fscontext.go",
|
||||
"getdents.go",
|
||||
"ioctl.go",
|
||||
"memfd.go",
|
||||
"mmap.go",
|
||||
"path.go",
|
||||
"pipe.go",
|
||||
@@ -43,6 +44,7 @@ go_library(
|
||||
"//pkg/sentry/fsimpl/pipefs",
|
||||
"//pkg/sentry/fsimpl/signalfd",
|
||||
"//pkg/sentry/fsimpl/timerfd",
|
||||
"//pkg/sentry/fsimpl/tmpfs",
|
||||
"//pkg/sentry/kernel",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
|
||||
@@ -17,6 +17,7 @@ package vfs2
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/pipe"
|
||||
slinux "gvisor.dev/gvisor/pkg/sentry/syscalls/linux"
|
||||
@@ -157,6 +158,15 @@ func Fcntl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
return 0, nil, syserror.EBADF
|
||||
}
|
||||
return uintptr(pipefile.PipeSize()), nil, nil
|
||||
case linux.F_GET_SEALS:
|
||||
val, err := tmpfs.GetSeals(file)
|
||||
return uintptr(val), nil, err
|
||||
case linux.F_ADD_SEALS:
|
||||
if !file.IsWritable() {
|
||||
return 0, nil, syserror.EPERM
|
||||
}
|
||||
err := tmpfs.AddSeals(file, args[2].Uint())
|
||||
return 0, nil, err
|
||||
default:
|
||||
// TODO(gvisor.dev/issue/1623): Everything else is not yet supported.
|
||||
return 0, nil, syserror.EINVAL
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package vfs2
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/sentry/arch"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/tmpfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
const (
|
||||
memfdPrefix = "memfd:"
|
||||
memfdMaxNameLen = linux.NAME_MAX - len(memfdPrefix)
|
||||
memfdAllFlags = uint32(linux.MFD_CLOEXEC | linux.MFD_ALLOW_SEALING)
|
||||
)
|
||||
|
||||
// MemfdCreate implements the linux syscall memfd_create(2).
|
||||
func MemfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
flags := args[1].Uint()
|
||||
|
||||
if flags&^memfdAllFlags != 0 {
|
||||
// Unknown bits in flags.
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
allowSeals := flags&linux.MFD_ALLOW_SEALING != 0
|
||||
cloExec := flags&linux.MFD_CLOEXEC != 0
|
||||
|
||||
name, err := t.CopyInString(addr, memfdMaxNameLen)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
shmMount := t.Kernel().ShmMount()
|
||||
file, err := tmpfs.NewMemfd(shmMount, t.Credentials(), allowSeals, memfdPrefix+name)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
fd, err := t.NewFDFromVFS2(0, file, kernel.FDFlags{
|
||||
CloseOnExec: cloExec,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return uintptr(fd), nil, nil
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func Override() {
|
||||
s.Table[306] = syscalls.Supported("syncfs", Syncfs)
|
||||
s.Table[307] = syscalls.Supported("sendmmsg", SendMMsg)
|
||||
s.Table[316] = syscalls.Supported("renameat2", Renameat2)
|
||||
delete(s.Table, 319) // memfd_create
|
||||
s.Table[319] = syscalls.Supported("memfd_create", MemfdCreate)
|
||||
s.Table[322] = syscalls.Supported("execveat", Execveat)
|
||||
s.Table[327] = syscalls.Supported("preadv2", Preadv2)
|
||||
s.Table[328] = syscalls.Supported("pwritev2", Pwritev2)
|
||||
|
||||
Reference in New Issue
Block a user