Source file src/os/pidfd_linux.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Support for pidfd was added during the course of a few Linux releases:
     6  //  v5.1: pidfd_send_signal syscall;
     7  //  v5.2: CLONE_PIDFD flag for clone syscall;
     8  //  v5.3: pidfd_open syscall, clone3 syscall;
     9  //  v5.4: P_PIDFD idtype support for waitid syscall;
    10  //  v5.6: pidfd_getfd syscall.
    11  //
    12  // N.B. Alternative Linux implementations may not follow this ordering. e.g.,
    13  // QEMU user mode 7.2 added pidfd_open, but CLONE_PIDFD was not added until
    14  // 8.0.
    15  
    16  package os
    17  
    18  import (
    19  	"errors"
    20  	"internal/syscall/unix"
    21  	"runtime"
    22  	"sync"
    23  	"syscall"
    24  	_ "unsafe" // for linkname
    25  )
    26  
    27  // ensurePidfd initializes the PidFD field in sysAttr if it is not already set.
    28  // It returns the original or modified SysProcAttr struct and a flag indicating
    29  // whether the PidFD should be duplicated before using.
    30  func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
    31  	if !pidfdWorks() {
    32  		return sysAttr, false
    33  	}
    34  
    35  	var pidfd int
    36  
    37  	if sysAttr == nil {
    38  		return &syscall.SysProcAttr{
    39  			PidFD: &pidfd,
    40  		}, false
    41  	}
    42  	if sysAttr.PidFD == nil {
    43  		newSys := *sysAttr // copy
    44  		newSys.PidFD = &pidfd
    45  		return &newSys, false
    46  	}
    47  
    48  	return sysAttr, true
    49  }
    50  
    51  // getPidfd returns the value of sysAttr.PidFD (or its duplicate if needDup is
    52  // set) and a flag indicating whether the value can be used.
    53  func getPidfd(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) {
    54  	if !pidfdWorks() {
    55  		return 0, false
    56  	}
    57  
    58  	h := *sysAttr.PidFD
    59  	if needDup {
    60  		dupH, e := unix.Fcntl(h, syscall.F_DUPFD_CLOEXEC, 0)
    61  		if e != nil {
    62  			return 0, false
    63  		}
    64  		h = dupH
    65  	}
    66  	return uintptr(h), true
    67  }
    68  
    69  func pidfdFind(pid int) (uintptr, error) {
    70  	if !pidfdWorks() {
    71  		return 0, syscall.ENOSYS
    72  	}
    73  
    74  	h, err := unix.PidFDOpen(pid, 0)
    75  	if err != nil {
    76  		return 0, convertESRCH(err)
    77  	}
    78  	return h, nil
    79  }
    80  
    81  func (p *Process) pidfdWait() (*ProcessState, error) {
    82  	// When pidfd is used, there is no wait/kill race (described in CL 23967)
    83  	// because the PID recycle issue doesn't exist (IOW, pidfd, unlike PID,
    84  	// is guaranteed to refer to one particular process). Thus, there is no
    85  	// need for the workaround (blockUntilWaitable + sigMu) from pidWait.
    86  	//
    87  	// We _do_ need to be careful about reuse of the pidfd FD number when
    88  	// closing the pidfd. See handle for more details.
    89  	handle, status := p.handleTransientAcquire()
    90  	switch status {
    91  	case statusDone:
    92  		// Process already completed Wait, or was not found by
    93  		// pidfdFind. Return ECHILD for consistency with what the wait
    94  		// syscall would return.
    95  		return nil, NewSyscallError("wait", syscall.ECHILD)
    96  	case statusReleased:
    97  		return nil, syscall.EINVAL
    98  	}
    99  	defer p.handleTransientRelease()
   100  
   101  	var (
   102  		info   unix.SiginfoChild
   103  		rusage syscall.Rusage
   104  	)
   105  	err := ignoringEINTR(func() error {
   106  		return unix.Waitid(unix.P_PIDFD, int(handle), &info, syscall.WEXITED, &rusage)
   107  	})
   108  	if err != nil {
   109  		return nil, NewSyscallError("waitid", err)
   110  	}
   111  	// Release the Process' handle reference, in addition to the reference
   112  	// we took above.
   113  	p.handlePersistentRelease(statusDone)
   114  	return &ProcessState{
   115  		pid:    int(info.Pid),
   116  		status: info.WaitStatus(),
   117  		rusage: &rusage,
   118  	}, nil
   119  }
   120  
   121  func (p *Process) pidfdSendSignal(s syscall.Signal) error {
   122  	handle, status := p.handleTransientAcquire()
   123  	switch status {
   124  	case statusDone:
   125  		return ErrProcessDone
   126  	case statusReleased:
   127  		return errors.New("os: process already released")
   128  	}
   129  	defer p.handleTransientRelease()
   130  
   131  	return convertESRCH(unix.PidFDSendSignal(handle, s))
   132  }
   133  
   134  func pidfdWorks() bool {
   135  	return checkPidfdOnce() == nil
   136  }
   137  
   138  var checkPidfdOnce = sync.OnceValue(checkPidfd)
   139  
   140  // checkPidfd checks whether all required pidfd-related syscalls work. This
   141  // consists of pidfd_open and pidfd_send_signal syscalls, waitid syscall with
   142  // idtype of P_PIDFD, and clone(CLONE_PIDFD).
   143  //
   144  // Reasons for non-working pidfd syscalls include an older kernel and an
   145  // execution environment in which the above system calls are restricted by
   146  // seccomp or a similar technology.
   147  func checkPidfd() error {
   148  	// In Android version < 12, pidfd-related system calls are not allowed
   149  	// by seccomp and trigger the SIGSYS signal. See issue #69065.
   150  	if runtime.GOOS == "android" {
   151  		ignoreSIGSYS()
   152  		defer restoreSIGSYS()
   153  	}
   154  
   155  	// Get a pidfd of the current process (opening of "/proc/self" won't
   156  	// work for waitid).
   157  	fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
   158  	if err != nil {
   159  		return NewSyscallError("pidfd_open", err)
   160  	}
   161  	defer syscall.Close(int(fd))
   162  
   163  	// Check waitid(P_PIDFD) works.
   164  	err = ignoringEINTR(func() error {
   165  		return unix.Waitid(unix.P_PIDFD, int(fd), nil, syscall.WEXITED, nil)
   166  	})
   167  	// Expect ECHILD from waitid since we're not our own parent.
   168  	if err != syscall.ECHILD {
   169  		return NewSyscallError("pidfd_wait", err)
   170  	}
   171  
   172  	// Check pidfd_send_signal works (should be able to send 0 to itself).
   173  	if err := unix.PidFDSendSignal(fd, 0); err != nil {
   174  		return NewSyscallError("pidfd_send_signal", err)
   175  	}
   176  
   177  	// Verify that clone(CLONE_PIDFD) works.
   178  	//
   179  	// This shouldn't be necessary since pidfd_open was added in Linux 5.3,
   180  	// after CLONE_PIDFD in Linux 5.2, but some alternative Linux
   181  	// implementations may not adhere to this ordering.
   182  	if err := checkClonePidfd(); err != nil {
   183  		return err
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // Provided by syscall.
   190  //
   191  //go:linkname checkClonePidfd
   192  func checkClonePidfd() error
   193  
   194  // Provided by runtime.
   195  //
   196  //go:linkname ignoreSIGSYS
   197  func ignoreSIGSYS()
   198  
   199  //go:linkname restoreSIGSYS
   200  func restoreSIGSYS()
   201  

View as plain text