github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/proc/tasks_files.go (about)

     1  // Copyright 2019 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  	"runtime"
    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/hostarch"
    27  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsimpl/kernfs"
    28  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel"
    29  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth"
    30  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/time"
    31  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/usage"
    32  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs"
    33  )
    34  
    35  // +stateify savable
    36  type selfSymlink struct {
    37  	implStatFS
    38  	kernfs.InodeAttrs
    39  	kernfs.InodeNoopRefCount
    40  	kernfs.InodeNotAnonymous
    41  	kernfs.InodeSymlink
    42  	kernfs.InodeWatches
    43  
    44  	pidns *kernel.PIDNamespace
    45  }
    46  
    47  var _ kernfs.Inode = (*selfSymlink)(nil)
    48  
    49  func (i *tasksInode) newSelfSymlink(ctx context.Context, creds *auth.Credentials) kernfs.Inode {
    50  	inode := &selfSymlink{pidns: i.pidns}
    51  	inode.Init(ctx, creds, linux.UNNAMED_MAJOR, i.fs.devMinor, i.fs.NextIno(), linux.ModeSymlink|0777)
    52  	return inode
    53  }
    54  
    55  func (s *selfSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error) {
    56  	t := kernel.TaskFromContext(ctx)
    57  	if t == nil {
    58  		// Who is reading this link?
    59  		return "", linuxerr.EINVAL
    60  	}
    61  	tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup())
    62  	if tgid == 0 {
    63  		return "", linuxerr.ENOENT
    64  	}
    65  	return strconv.FormatUint(uint64(tgid), 10), nil
    66  }
    67  
    68  func (s *selfSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) {
    69  	target, err := s.Readlink(ctx, mnt)
    70  	return vfs.VirtualDentry{}, target, err
    71  }
    72  
    73  // SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
    74  func (*selfSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
    75  	return linuxerr.EPERM
    76  }
    77  
    78  // +stateify savable
    79  type threadSelfSymlink struct {
    80  	implStatFS
    81  	kernfs.InodeAttrs
    82  	kernfs.InodeNoopRefCount
    83  	kernfs.InodeNotAnonymous
    84  	kernfs.InodeSymlink
    85  	kernfs.InodeWatches
    86  
    87  	pidns *kernel.PIDNamespace
    88  }
    89  
    90  var _ kernfs.Inode = (*threadSelfSymlink)(nil)
    91  
    92  func (i *tasksInode) newThreadSelfSymlink(ctx context.Context, creds *auth.Credentials) kernfs.Inode {
    93  	inode := &threadSelfSymlink{pidns: i.pidns}
    94  	inode.Init(ctx, creds, linux.UNNAMED_MAJOR, i.fs.devMinor, i.fs.NextIno(), linux.ModeSymlink|0777)
    95  	return inode
    96  }
    97  
    98  func (s *threadSelfSymlink) Readlink(ctx context.Context, _ *vfs.Mount) (string, error) {
    99  	t := kernel.TaskFromContext(ctx)
   100  	if t == nil {
   101  		// Who is reading this link?
   102  		return "", linuxerr.EINVAL
   103  	}
   104  	tgid := s.pidns.IDOfThreadGroup(t.ThreadGroup())
   105  	tid := s.pidns.IDOfTask(t)
   106  	if tid == 0 || tgid == 0 {
   107  		return "", linuxerr.ENOENT
   108  	}
   109  	return fmt.Sprintf("%d/task/%d", tgid, tid), nil
   110  }
   111  
   112  func (s *threadSelfSymlink) Getlink(ctx context.Context, mnt *vfs.Mount) (vfs.VirtualDentry, string, error) {
   113  	target, err := s.Readlink(ctx, mnt)
   114  	return vfs.VirtualDentry{}, target, err
   115  }
   116  
   117  // SetStat implements kernfs.Inode.SetStat not allowing inode attributes to be changed.
   118  func (*threadSelfSymlink) SetStat(context.Context, *vfs.Filesystem, *auth.Credentials, vfs.SetStatOptions) error {
   119  	return linuxerr.EPERM
   120  }
   121  
   122  // dynamicBytesFileSetAttr implements a special file that allows inode
   123  // attributes to be set. This is to support /proc files that are readonly, but
   124  // allow attributes to be set.
   125  //
   126  // +stateify savable
   127  type dynamicBytesFileSetAttr struct {
   128  	kernfs.DynamicBytesFile
   129  }
   130  
   131  // SetStat implements kernfs.Inode.SetStat.
   132  func (d *dynamicBytesFileSetAttr) SetStat(ctx context.Context, fs *vfs.Filesystem, creds *auth.Credentials, opts vfs.SetStatOptions) error {
   133  	return d.DynamicBytesFile.InodeAttrs.SetStat(ctx, fs, creds, opts)
   134  }
   135  
   136  // cpuStats contains the breakdown of CPU time for /proc/stat.
   137  //
   138  // +stateify savable
   139  type cpuStats struct {
   140  	// user is time spent in userspace tasks with non-positive niceness.
   141  	user uint64
   142  
   143  	// nice is time spent in userspace tasks with positive niceness.
   144  	nice uint64
   145  
   146  	// system is time spent in non-interrupt kernel context.
   147  	system uint64
   148  
   149  	// idle is time spent idle.
   150  	idle uint64
   151  
   152  	// ioWait is time spent waiting for IO.
   153  	ioWait uint64
   154  
   155  	// irq is time spent in interrupt context.
   156  	irq uint64
   157  
   158  	// softirq is time spent in software interrupt context.
   159  	softirq uint64
   160  
   161  	// steal is involuntary wait time.
   162  	steal uint64
   163  
   164  	// guest is time spent in guests with non-positive niceness.
   165  	guest uint64
   166  
   167  	// guestNice is time spent in guests with positive niceness.
   168  	guestNice uint64
   169  }
   170  
   171  // String implements fmt.Stringer.
   172  func (c cpuStats) String() string {
   173  	return fmt.Sprintf("%d %d %d %d %d %d %d %d %d %d", c.user, c.nice, c.system, c.idle, c.ioWait, c.irq, c.softirq, c.steal, c.guest, c.guestNice)
   174  }
   175  
   176  // statData implements vfs.DynamicBytesSource for /proc/stat.
   177  //
   178  // +stateify savable
   179  type statData struct {
   180  	dynamicBytesFileSetAttr
   181  }
   182  
   183  var _ dynamicInode = (*statData)(nil)
   184  
   185  // Generate implements vfs.DynamicBytesSource.Generate.
   186  func (*statData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   187  	// TODO(b/37226836): We currently export only zero CPU stats. We could
   188  	// at least provide some aggregate stats.
   189  	var cpu cpuStats
   190  	fmt.Fprintf(buf, "cpu  %s\n", cpu)
   191  
   192  	k := kernel.KernelFromContext(ctx)
   193  	for c, max := uint(0), k.ApplicationCores(); c < max; c++ {
   194  		fmt.Fprintf(buf, "cpu%d %s\n", c, cpu)
   195  	}
   196  
   197  	// The total number of interrupts is dependent on the CPUs and PCI
   198  	// devices on the system. See arch_probe_nr_irqs.
   199  	//
   200  	// Since we don't report real interrupt stats, just choose an arbitrary
   201  	// value from a representative VM.
   202  	const numInterrupts = 256
   203  
   204  	// The Kernel doesn't handle real interrupts, so report all zeroes.
   205  	// TODO(b/37226836): We could count page faults as #PF.
   206  	fmt.Fprintf(buf, "intr 0") // total
   207  	for i := 0; i < numInterrupts; i++ {
   208  		fmt.Fprintf(buf, " 0")
   209  	}
   210  	fmt.Fprintf(buf, "\n")
   211  
   212  	// Total number of context switches.
   213  	// TODO(b/37226836): Count this.
   214  	fmt.Fprintf(buf, "ctxt 0\n")
   215  
   216  	// CLOCK_REALTIME timestamp from boot, in seconds.
   217  	fmt.Fprintf(buf, "btime %d\n", k.Timekeeper().BootTime().Seconds())
   218  
   219  	// Total number of clones.
   220  	// TODO(b/37226836): Count this.
   221  	fmt.Fprintf(buf, "processes 0\n")
   222  
   223  	// Number of runnable tasks.
   224  	// TODO(b/37226836): Count this.
   225  	fmt.Fprintf(buf, "procs_running 0\n")
   226  
   227  	// Number of tasks waiting on IO.
   228  	// TODO(b/37226836): Count this.
   229  	fmt.Fprintf(buf, "procs_blocked 0\n")
   230  
   231  	// Number of each softirq handled.
   232  	fmt.Fprintf(buf, "softirq 0") // total
   233  	for i := 0; i < linux.NumSoftIRQ; i++ {
   234  		fmt.Fprintf(buf, " 0")
   235  	}
   236  	fmt.Fprintf(buf, "\n")
   237  	return nil
   238  }
   239  
   240  // loadavgData backs /proc/loadavg.
   241  //
   242  // +stateify savable
   243  type loadavgData struct {
   244  	dynamicBytesFileSetAttr
   245  }
   246  
   247  var _ dynamicInode = (*loadavgData)(nil)
   248  
   249  // Generate implements vfs.DynamicBytesSource.Generate.
   250  func (*loadavgData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   251  	// TODO(b/62345059): Include real data in fields.
   252  	// Column 1-3: CPU and IO utilization of the last 1, 5, and 10 minute periods.
   253  	// Column 4-5: currently running processes and the total number of processes.
   254  	// Column 6: the last process ID used.
   255  	fmt.Fprintf(buf, "%.2f %.2f %.2f %d/%d %d\n", 0.00, 0.00, 0.00, 0, 0, 0)
   256  	return nil
   257  }
   258  
   259  // meminfoData implements vfs.DynamicBytesSource for /proc/meminfo.
   260  //
   261  // +stateify savable
   262  type meminfoData struct {
   263  	dynamicBytesFileSetAttr
   264  }
   265  
   266  var _ dynamicInode = (*meminfoData)(nil)
   267  
   268  // Generate implements vfs.DynamicBytesSource.Generate.
   269  func (*meminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   270  	mf := kernel.KernelFromContext(ctx).MemoryFile()
   271  	_ = mf.UpdateUsage() // Best effort
   272  	snapshot, totalUsage := usage.MemoryAccounting.Copy()
   273  	totalSize := usage.TotalMemory(mf.TotalSize(), totalUsage)
   274  	anon := snapshot.Anonymous + snapshot.Tmpfs
   275  	file := snapshot.PageCache + snapshot.Mapped
   276  	// We don't actually have active/inactive LRUs, so just make up numbers.
   277  	activeFile := (file / 2) &^ (hostarch.PageSize - 1)
   278  	inactiveFile := file - activeFile
   279  
   280  	fmt.Fprintf(buf, "MemTotal:       %8d kB\n", totalSize/1024)
   281  	memFree := totalSize - totalUsage
   282  	if memFree > totalSize {
   283  		// Underflow.
   284  		memFree = 0
   285  	}
   286  	// We use MemFree as MemAvailable because we don't swap.
   287  	// TODO(rahat): When reclaim is implemented the value of MemAvailable
   288  	// should change.
   289  	fmt.Fprintf(buf, "MemFree:        %8d kB\n", memFree/1024)
   290  	fmt.Fprintf(buf, "MemAvailable:   %8d kB\n", memFree/1024)
   291  	fmt.Fprintf(buf, "Buffers:               0 kB\n") // memory usage by block devices
   292  	fmt.Fprintf(buf, "Cached:         %8d kB\n", (file+snapshot.Tmpfs)/1024)
   293  	// Emulate a system with no swap, which disables inactivation of anon pages.
   294  	fmt.Fprintf(buf, "SwapCache:             0 kB\n")
   295  	fmt.Fprintf(buf, "Active:         %8d kB\n", (anon+activeFile)/1024)
   296  	fmt.Fprintf(buf, "Inactive:       %8d kB\n", inactiveFile/1024)
   297  	fmt.Fprintf(buf, "Active(anon):   %8d kB\n", anon/1024)
   298  	fmt.Fprintf(buf, "Inactive(anon):        0 kB\n")
   299  	fmt.Fprintf(buf, "Active(file):   %8d kB\n", activeFile/1024)
   300  	fmt.Fprintf(buf, "Inactive(file): %8d kB\n", inactiveFile/1024)
   301  	fmt.Fprintf(buf, "Unevictable:           0 kB\n") // TODO(b/31823263)
   302  	fmt.Fprintf(buf, "Mlocked:               0 kB\n") // TODO(b/31823263)
   303  	fmt.Fprintf(buf, "SwapTotal:             0 kB\n")
   304  	fmt.Fprintf(buf, "SwapFree:              0 kB\n")
   305  	fmt.Fprintf(buf, "Dirty:                 0 kB\n")
   306  	fmt.Fprintf(buf, "Writeback:             0 kB\n")
   307  	fmt.Fprintf(buf, "AnonPages:      %8d kB\n", anon/1024)
   308  	fmt.Fprintf(buf, "Mapped:         %8d kB\n", file/1024) // doesn't count mapped tmpfs, which we don't know
   309  	fmt.Fprintf(buf, "Shmem:          %8d kB\n", snapshot.Tmpfs/1024)
   310  	return nil
   311  }
   312  
   313  // uptimeData implements vfs.DynamicBytesSource for /proc/uptime.
   314  //
   315  // +stateify savable
   316  type uptimeData struct {
   317  	dynamicBytesFileSetAttr
   318  }
   319  
   320  var _ dynamicInode = (*uptimeData)(nil)
   321  
   322  // Generate implements vfs.DynamicBytesSource.Generate.
   323  func (*uptimeData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   324  	k := kernel.KernelFromContext(ctx)
   325  	now := time.NowFromContext(ctx)
   326  
   327  	// Pretend that we've spent zero time sleeping (second number).
   328  	fmt.Fprintf(buf, "%.2f 0.00\n", now.Sub(k.Timekeeper().BootTime()).Seconds())
   329  	return nil
   330  }
   331  
   332  // versionData implements vfs.DynamicBytesSource for /proc/version.
   333  //
   334  // +stateify savable
   335  type versionData struct {
   336  	dynamicBytesFileSetAttr
   337  }
   338  
   339  var _ dynamicInode = (*versionData)(nil)
   340  
   341  // Generate implements vfs.DynamicBytesSource.Generate.
   342  func (*versionData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   343  	// /proc/version takes the form:
   344  	//
   345  	// "SYSNAME version RELEASE (COMPILE_USER@COMPILE_HOST)
   346  	// (COMPILER_VERSION) VERSION"
   347  	//
   348  	// where:
   349  	//	- SYSNAME, RELEASE, and VERSION are the same as returned by
   350  	//		sys_utsname
   351  	//	- COMPILE_USER is the user that build the kernel
   352  	//	- COMPILE_HOST is the hostname of the machine on which the kernel
   353  	//		was built
   354  	//	- COMPILER_VERSION is the version reported by the building compiler
   355  	//
   356  	// Since we don't really want to expose build information to
   357  	// applications, those fields are omitted.
   358  	//
   359  	// FIXME(mpratt): Using Version from the init task SyscallTable
   360  	// disregards the different version a task may have (e.g., in a uts
   361  	// namespace).
   362  	ver := kernelVersion(ctx)
   363  	fmt.Fprintf(buf, "%s version %s %s\n", ver.Sysname, ver.Release, ver.Version)
   364  	return nil
   365  }
   366  
   367  // filesystemsData backs /proc/filesystems.
   368  //
   369  // +stateify savable
   370  type filesystemsData struct {
   371  	kernfs.DynamicBytesFile
   372  }
   373  
   374  var _ dynamicInode = (*filesystemsData)(nil)
   375  
   376  // Generate implements vfs.DynamicBytesSource.Generate.
   377  func (d *filesystemsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   378  	k := kernel.KernelFromContext(ctx)
   379  	k.VFS().GenerateProcFilesystems(buf)
   380  	return nil
   381  }
   382  
   383  // cgroupsData backs /proc/cgroups.
   384  //
   385  // +stateify savable
   386  type cgroupsData struct {
   387  	dynamicBytesFileSetAttr
   388  }
   389  
   390  var _ dynamicInode = (*cgroupsData)(nil)
   391  
   392  // Generate implements vfs.DynamicBytesSource.Generate.
   393  func (*cgroupsData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   394  	r := kernel.KernelFromContext(ctx).CgroupRegistry()
   395  	r.GenerateProcCgroups(buf)
   396  	return nil
   397  }
   398  
   399  // cmdLineData backs /proc/cmdline.
   400  //
   401  // +stateify savable
   402  type cmdLineData struct {
   403  	dynamicBytesFileSetAttr
   404  }
   405  
   406  var _ dynamicInode = (*cmdLineData)(nil)
   407  
   408  // Generate implements vfs.DynamicByteSource.Generate.
   409  func (*cmdLineData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   410  	fmt.Fprintf(buf, "BOOT_IMAGE=/vmlinuz-%s-gvisor quiet\n", kernelVersion(ctx).Release)
   411  	return nil
   412  }
   413  
   414  // kernelVersion returns the kernel version.
   415  func kernelVersion(ctx context.Context) kernel.Version {
   416  	k := kernel.KernelFromContext(ctx)
   417  	init := k.GlobalInit()
   418  	if init == nil {
   419  		// Attempted to read before the init Task is created. This can
   420  		// only occur during startup, which should never need to read
   421  		// this file.
   422  		panic("Attempted to read version before initial Task is available")
   423  	}
   424  	return init.Leader().SyscallTable().Version
   425  }
   426  
   427  // sentryMeminfoData implements vfs.DynamicBytesSource for /proc/sentry-meminfo.
   428  //
   429  // +stateify savable
   430  type sentryMeminfoData struct {
   431  	dynamicBytesFileSetAttr
   432  }
   433  
   434  var _ dynamicInode = (*sentryMeminfoData)(nil)
   435  
   436  // Generate implements vfs.DynamicBytesSource.Generate.
   437  func (*sentryMeminfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
   438  	var sentryMeminfo runtime.MemStats
   439  	runtime.ReadMemStats(&sentryMeminfo)
   440  
   441  	fmt.Fprintf(buf, "Alloc:          %8d kB\n", sentryMeminfo.Alloc/1024)
   442  	fmt.Fprintf(buf, "TotalAlloc:     %8d kB\n", sentryMeminfo.TotalAlloc/1024)
   443  	fmt.Fprintf(buf, "Sys:            %8d kB\n", sentryMeminfo.Sys/1024)
   444  	fmt.Fprintf(buf, "Mallocs:        %8d\n", sentryMeminfo.Mallocs)
   445  	fmt.Fprintf(buf, "Frees:          %8d\n", sentryMeminfo.Frees)
   446  	fmt.Fprintf(buf, "Live Objects:   %8d\n", sentryMeminfo.Mallocs-sentryMeminfo.Frees)
   447  	fmt.Fprintf(buf, "HeapAlloc:      %8d kB\n", sentryMeminfo.HeapAlloc/1024)
   448  	fmt.Fprintf(buf, "HeapSys:        %8d kB\n", sentryMeminfo.HeapSys/1024)
   449  	fmt.Fprintf(buf, "HeapObjects:    %8d\n", sentryMeminfo.HeapObjects)
   450  	return nil
   451  }