...

Source file src/github.com/opencontainers/runc/libcontainer/utils/utils_unix.go

Documentation: github.com/opencontainers/runc/libcontainer/utils

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package utils
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"strconv"
    10  	_ "unsafe" // for go:linkname
    11  
    12  	"golang.org/x/sys/unix"
    13  )
    14  
    15  // EnsureProcHandle returns whether or not the given file handle is on procfs.
    16  func EnsureProcHandle(fh *os.File) error {
    17  	var buf unix.Statfs_t
    18  	if err := unix.Fstatfs(int(fh.Fd()), &buf); err != nil {
    19  		return fmt.Errorf("ensure %s is on procfs: %w", fh.Name(), err)
    20  	}
    21  	if buf.Type != unix.PROC_SUPER_MAGIC {
    22  		return fmt.Errorf("%s is not on procfs", fh.Name())
    23  	}
    24  	return nil
    25  }
    26  
    27  type fdFunc func(fd int)
    28  
    29  // fdRangeFrom calls the passed fdFunc for each file descriptor that is open in
    30  // the current process.
    31  func fdRangeFrom(minFd int, fn fdFunc) error {
    32  	fdDir, err := os.Open("/proc/self/fd")
    33  	if err != nil {
    34  		return err
    35  	}
    36  	defer fdDir.Close()
    37  
    38  	if err := EnsureProcHandle(fdDir); err != nil {
    39  		return err
    40  	}
    41  
    42  	fdList, err := fdDir.Readdirnames(-1)
    43  	if err != nil {
    44  		return err
    45  	}
    46  	for _, fdStr := range fdList {
    47  		fd, err := strconv.Atoi(fdStr)
    48  		// Ignore non-numeric file names.
    49  		if err != nil {
    50  			continue
    51  		}
    52  		// Ignore descriptors lower than our specified minimum.
    53  		if fd < minFd {
    54  			continue
    55  		}
    56  		// Ignore the file descriptor we used for readdir, as it will be closed
    57  		// when we return.
    58  		if uintptr(fd) == fdDir.Fd() {
    59  			continue
    60  		}
    61  		// Run the closure.
    62  		fn(fd)
    63  	}
    64  	return nil
    65  }
    66  
    67  // CloseExecFrom sets the O_CLOEXEC flag on all file descriptors greater or
    68  // equal to minFd in the current process.
    69  func CloseExecFrom(minFd int) error {
    70  	return fdRangeFrom(minFd, unix.CloseOnExec)
    71  }
    72  
    73  //go:linkname runtime_IsPollDescriptor internal/poll.IsPollDescriptor
    74  
    75  // In order to make sure we do not close the internal epoll descriptors the Go
    76  // runtime uses, we need to ensure that we skip descriptors that match
    77  // "internal/poll".IsPollDescriptor. Yes, this is a Go runtime internal thing,
    78  // unfortunately there's no other way to be sure we're only keeping the file
    79  // descriptors the Go runtime needs. Hopefully nothing blows up doing this...
    80  func runtime_IsPollDescriptor(fd uintptr) bool //nolint:revive
    81  
    82  // UnsafeCloseFrom closes all file descriptors greater or equal to minFd in the
    83  // current process, except for those critical to Go's runtime (such as the
    84  // netpoll management descriptors).
    85  //
    86  // NOTE: That this function is incredibly dangerous to use in most Go code, as
    87  // closing file descriptors from underneath *os.File handles can lead to very
    88  // bad behaviour (the closed file descriptor can be re-used and then any
    89  // *os.File operations would apply to the wrong file). This function is only
    90  // intended to be called from the last stage of runc init.
    91  func UnsafeCloseFrom(minFd int) error {
    92  	// We must not close some file descriptors.
    93  	return fdRangeFrom(minFd, func(fd int) {
    94  		if runtime_IsPollDescriptor(uintptr(fd)) {
    95  			// These are the Go runtimes internal netpoll file descriptors.
    96  			// These file descriptors are operated on deep in the Go scheduler,
    97  			// and closing those files from underneath Go can result in panics.
    98  			// There is no issue with keeping them because they are not
    99  			// executable and are not useful to an attacker anyway. Also we
   100  			// don't have any choice.
   101  			return
   102  		}
   103  		// There's nothing we can do about errors from close(2), and the
   104  		// only likely error to be seen is EBADF which indicates the fd was
   105  		// already closed (in which case, we got what we wanted).
   106  		_ = unix.Close(fd)
   107  	})
   108  }
   109  
   110  // NewSockPair returns a new unix socket pair
   111  func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
   112  	fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
   113  	if err != nil {
   114  		return nil, nil, err
   115  	}
   116  	return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
   117  }
   118  

View as plain text