github.com/metacubex/gvisor@v0.0.0-20240320004321-933faba989ec/pkg/sentry/fsimpl/proc/task_fds.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package proc
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"sort"
    21  	"strconv"
    22  
    23  	"github.com/metacubex/gvisor/pkg/abi/linux"
    24  	"github.com/metacubex/gvisor/pkg/context"
    25  	"github.com/metacubex/gvisor/pkg/errors/linuxerr"
    26  	"github.com/metacubex/gvisor/pkg/sentry/fsimpl/kernfs"
    27  	"github.com/metacubex/gvisor/pkg/sentry/kernel"
    28  	"github.com/metacubex/gvisor/pkg/sentry/kernel/auth"
    29  	"github.com/metacubex/gvisor/pkg/sentry/vfs"
    30  )
    31  
    32  func getTaskFD(t *kernel.Task, fd int32) (*vfs.FileDescription, kernel.FDFlags) {
    33  	var (
    34  		file  *vfs.FileDescription
    35  		flags kernel.FDFlags
    36  	)
    37  	t.WithMuLocked(func(t *kernel.Task) {
    38  		if fdt := t.FDTable(); fdt != nil {
    39  			file, flags = fdt.Get(fd)
    40  		}
    41  	})
    42  	return file, flags
    43  }
    44  
    45  func taskFDExists(ctx context.Context, fs *filesystem, t *kernel.Task, fd int32) bool {
    46  	var exists bool
    47  	t.WithMuLocked(func(task *kernel.Task) {
    48  		if fdt := t.FDTable(); fdt != nil {
    49  			exists = fdt.Exists(fd)
    50  		}
    51  	})
    52  	return exists
    53  }
    54  
    55  // +stateify savable
    56  type fdDir struct {
    57  	locks vfs.FileLocks
    58  
    59  	fs   *filesystem
    60  	task *kernel.Task
    61  
    62  	// When produceSymlinks is set, dirents produces for the FDs are reported
    63  	// as symlink. Otherwise, they are reported as regular files.
    64  	produceSymlink bool
    65  }
    66  
    67  // IterDirents implements kernfs.inodeDirectory.IterDirents.
    68  func (i *fdDir) IterDirents(ctx context.Context, mnt *vfs.Mount, cb vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) {
    69  	var fds []int32
    70  	i.task.WithMuLocked(func(t *kernel.Task) {
    71  		if fdTable := t.FDTable(); fdTable != nil {
    72  			fds = fdTable.GetFDs(ctx)
    73  		}
    74  	})
    75  
    76  	typ := uint8(linux.DT_REG)
    77  	if i.produceSymlink {
    78  		typ = linux.DT_LNK
    79  	}
    80  
    81  	// Find the appropriate starting point.
    82  	idx := sort.Search(len(fds), func(i int) bool { return fds[i] >= int32(relOffset) })
    83  	if idx >= len(fds) {
    84  		return offset, nil
    85  	}
    86  	for _, fd := range fds[idx:] {
    87  		dirent := vfs.Dirent{
    88  			Name:    strconv.FormatUint(uint64(fd), 10),
    89  			Type:    typ,
    90  			Ino:     i.fs.NextIno(),
    91  			NextOff: int64(fd) + 3,
    92  		}
    93  		if err := cb.Handle(dirent); err != nil {
    94  			// Getdents should iterate correctly despite mutation
    95  			// of fds, so we return the next fd to serialize plus
    96  			// 2 (which accounts for the "." and ".." tracked by
    97  			// kernfs) as the offset.
    98  			return int64(fd) + 2, err
    99  		}
   100  	}
   101  	// We serialized them all.  Next offset should be higher than last
   102  	// serialized fd.
   103  	return int64(fds[len(fds)-1]) + 3, nil
   104  }
   105  
   106  // fdDirInode represents the inode for /proc/[pid]/fd directory.
   107  //
   108  // +stateify savable
   109  type fdDirInode struct {
   110  	fdDir
   111  	fdDirInodeRefs
   112  	implStatFS
   113  	kernfs.InodeAlwaysValid
   114  	kernfs.InodeAttrs
   115  	kernfs.InodeDirectoryNoNewChildren
   116  	kernfs.InodeNotAnonymous
   117  	kernfs.InodeNotSymlink
   118  	kernfs.InodeTemporary
   119  	kernfs.InodeWatches
   120  	kernfs.OrderedChildren
   121  }
   122  
   123  var _ kernfs.Inode = (*fdDirInode)(nil)
   124  
   125  func (fs *filesystem) newFDDirInode(ctx context.Context, task *kernel.Task) kernfs.Inode {
   126  	inode := &fdDirInode{
   127  		fdDir: fdDir{
   128  			fs:             fs,
   129  			task:           task,
   130  			produceSymlink: true,
   131  		},
   132  	}
   133  	inode.InodeAttrs.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0555)
   134  	inode.InitRefs()
   135  	inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
   136  	return inode
   137  }
   138  
   139  // IterDirents implements kernfs.inodeDirectory.IterDirents.
   140  func (i *fdDirInode) IterDirents(ctx context.Context, mnt *vfs.Mount, cb vfs.IterDirentsCallback, offset, relOffset int64) (int64, error) {
   141  	return i.fdDir.IterDirents(ctx, mnt, cb, offset, relOffset)
   142  }
   143  
   144  // Lookup implements kernfs.inodeDirectory.Lookup.
   145  func (i *fdDirInode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) {
   146  	fdInt, err := strconv.ParseInt(name, 10, 32)
   147  	if err != nil {
   148  		return nil, linuxerr.ENOENT
   149  	}
   150  	fd := int32(fdInt)
   151  	if !taskFDExists(ctx, i.fs, i.task, fd) {
   152  		return nil, linuxerr.ENOENT
   153  	}
   154  	return i.fs.newFDSymlink(ctx, i.task, fd, i.fs.NextIno()), nil
   155  }
   156  
   157  // Open implements kernfs.Inode.Open.
   158  func (i *fdDirInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   159  	fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), d, &i.OrderedChildren, &i.locks, &opts, kernfs.GenericDirectoryFDOptions{
   160  		SeekEnd: kernfs.SeekEndZero,
   161  	})
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  	return fd.VFSFileDescription(), nil
   166  }
   167  
   168  // CheckPermissions implements kernfs.Inode.CheckPermissions.
   169  //
   170  // This is to match Linux, which uses a special permission handler to guarantee
   171  // that a process can still access /proc/self/fd after it has executed
   172  // setuid. See fs/proc/fd.c:proc_fd_permission.
   173  func (i *fdDirInode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
   174  	err := i.InodeAttrs.CheckPermissions(ctx, creds, ats)
   175  	if err == nil {
   176  		// Access granted, no extra check needed.
   177  		return nil
   178  	}
   179  	if t := kernel.TaskFromContext(ctx); t != nil {
   180  		// Allow access if the task trying to access it is in the thread group
   181  		// corresponding to this directory.
   182  		if i.task.ThreadGroup() == t.ThreadGroup() {
   183  			// Access granted (overridden).
   184  			return nil
   185  		}
   186  	}
   187  	return err
   188  }
   189  
   190  // DecRef implements kernfs.Inode.DecRef.
   191  func (i *fdDirInode) DecRef(ctx context.Context) {
   192  	i.fdDirInodeRefs.DecRef(func() { i.Destroy(ctx) })
   193  }
   194  
   195  // fdSymlink is an symlink for the /proc/[pid]/fd/[fd] file.
   196  //
   197  // +stateify savable
   198  type fdSymlink struct {
   199  	implStatFS
   200  	kernfs.InodeAttrs
   201  	kernfs.InodeNoopRefCount
   202  	kernfs.InodeNotAnonymous
   203  	kernfs.InodeSymlink
   204  	kernfs.InodeWatches
   205  
   206  	fs   *filesystem
   207  	task *kernel.Task
   208  	fd   int32
   209  }
   210  
   211  var _ kernfs.Inode = (*fdSymlink)(nil)
   212  
   213  func (fs *filesystem) newFDSymlink(ctx context.Context, task *kernel.Task, fd int32, ino uint64) kernfs.Inode {
   214  	inode := &fdSymlink{
   215  		fs:   fs,
   216  		task: task,
   217  		fd:   fd,
   218  	}
   219  	inode.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, ino, linux.ModeSymlink|0777)
   220  	return inode
   221  }
   222  
   223  func (s *fdSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error) {
   224  	file, _ := getTaskFD(s.task, s.fd)
   225  	if file == nil {
   226  		return "", linuxerr.ENOENT
   227  	}
   228  	defer s.fs.SafeDecRefFD(ctx, file)
   229  	root := vfs.RootFromContext(ctx)
   230  	defer s.fs.SafeDecRef(ctx, root)
   231  
   232  	// Note: it's safe to reenter kernfs from Readlink if needed to resolve path.
   233  	return s.task.Kernel().VFS().PathnameWithDeleted(ctx, root, file.VirtualDentry())
   234  }
   235  
   236  func (s *fdSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) {
   237  	file, _ := getTaskFD(s.task, s.fd)
   238  	if file == nil {
   239  		return vfs.VirtualDentry{}, "", linuxerr.ENOENT
   240  	}
   241  	defer s.fs.SafeDecRefFD(ctx, file)
   242  	vd := file.VirtualDentry()
   243  	vd.IncRef()
   244  	return vd, "", nil
   245  }
   246  
   247  // Valid implements kernfs.Inode.Valid.
   248  func (s *fdSymlink) Valid(ctx context.Context) bool {
   249  	return taskFDExists(ctx, s.fs, s.task, s.fd)
   250  }
   251  
   252  // fdInfoDirInode represents the inode for /proc/[pid]/fdinfo directory.
   253  //
   254  // +stateify savable
   255  type fdInfoDirInode struct {
   256  	fdDir
   257  	fdInfoDirInodeRefs
   258  	implStatFS
   259  	kernfs.InodeAlwaysValid
   260  	kernfs.InodeAttrs
   261  	kernfs.InodeDirectoryNoNewChildren
   262  	kernfs.InodeNotAnonymous
   263  	kernfs.InodeNotSymlink
   264  	kernfs.InodeTemporary
   265  	kernfs.InodeWatches
   266  	kernfs.OrderedChildren
   267  }
   268  
   269  var _ kernfs.Inode = (*fdInfoDirInode)(nil)
   270  
   271  func (fs *filesystem) newFDInfoDirInode(ctx context.Context, task *kernel.Task) kernfs.Inode {
   272  	inode := &fdInfoDirInode{
   273  		fdDir: fdDir{
   274  			fs:   fs,
   275  			task: task,
   276  		},
   277  	}
   278  	inode.InodeAttrs.Init(ctx, task.Credentials(), linux.UNNAMED_MAJOR, fs.devMinor, fs.NextIno(), linux.ModeDirectory|0555)
   279  	inode.InitRefs()
   280  	inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})
   281  	return inode
   282  }
   283  
   284  // Lookup implements kernfs.inodeDirectory.Lookup.
   285  func (i *fdInfoDirInode) Lookup(ctx context.Context, name string) (kernfs.Inode, error) {
   286  	fdInt, err := strconv.ParseInt(name, 10, 32)
   287  	if err != nil {
   288  		return nil, linuxerr.ENOENT
   289  	}
   290  	fd := int32(fdInt)
   291  	if !taskFDExists(ctx, i.fs, i.task, fd) {
   292  		return nil, linuxerr.ENOENT
   293  	}
   294  	data := &fdInfoData{
   295  		fs:   i.fs,
   296  		task: i.task,
   297  		fd:   fd,
   298  	}
   299  	return i.fs.newTaskOwnedInode(ctx, i.task, i.fs.NextIno(), 0444, data), nil
   300  }
   301  
   302  // IterDirents implements Inode.IterDirents.
   303  func (i *fdInfoDirInode) IterDirents(ctx context.Context, mnt *vfs.Mount, cb vfs.IterDirentsCallback, offset, relOffset int64) (newOffset int64, err error) {
   304  	return i.fdDir.IterDirents(ctx, mnt, cb, offset, relOffset)
   305  }
   306  
   307  // Open implements kernfs.Inode.Open.
   308  func (i *fdInfoDirInode) Open(ctx context.Context, rp *vfs.ResolvingPath, d *kernfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
   309  	fd, err := kernfs.NewGenericDirectoryFD(rp.Mount(), d, &i.OrderedChildren, &i.locks, &opts, kernfs.GenericDirectoryFDOptions{
   310  		SeekEnd: kernfs.SeekEndZero,
   311  	})
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  	return fd.VFSFileDescription(), nil
   316  }
   317  
   318  // DecRef implements kernfs.Inode.DecRef.
   319  func (i *fdInfoDirInode) DecRef(ctx context.Context) {
   320  	i.fdInfoDirInodeRefs.DecRef(func() { i.Destroy(ctx) })
   321  }
   322  
   323  // fdInfoData implements vfs.DynamicBytesSource for /proc/[pid]/fdinfo/[fd].
   324  //
   325  // +stateify savable
   326  type fdInfoData struct {
   327  	kernfs.DynamicBytesFile
   328  
   329  	fs   *filesystem
   330  	task *kernel.Task
   331  	fd   int32
   332  }
   333  
   334  var _ dynamicInode = (*fdInfoData)(nil)
   335  
   336  // Generate implements vfs.DynamicBytesSource.Generate.
   337  func (d *fdInfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   338  	file, descriptorFlags := getTaskFD(d.task, d.fd)
   339  	if file == nil {
   340  		return linuxerr.ENOENT
   341  	}
   342  	defer d.fs.SafeDecRefFD(ctx, file)
   343  	// TODO(b/121266871): Include pos, locks, and other data. For now we only
   344  	// have flags.
   345  	// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
   346  	flags := uint(file.StatusFlags()) | descriptorFlags.ToLinuxFileFlags()
   347  	fmt.Fprintf(buf, "flags:\t0%o\n", flags)
   348  	return nil
   349  }
   350  
   351  // Valid implements kernfs.Inode.Valid.
   352  func (d *fdInfoData) Valid(ctx context.Context) bool {
   353  	return taskFDExists(ctx, d.fs, d.task, d.fd)
   354  }