utils_unix.go 3.7 KB

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