github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/pkg/abi/linux" 24 "github.com/nicocha30/gvisor-ligolo/pkg/context" 25 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 26 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsimpl/kernfs" 27 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 28 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 29 "github.com/nicocha30/gvisor-ligolo/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 }