Source File
pidfd_linux.go
Belonging Package
os
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Support for pidfd was added during the course of a few Linux releases:
// v5.1: pidfd_send_signal syscall;
// v5.2: CLONE_PIDFD flag for clone syscall;
// v5.3: pidfd_open syscall, clone3 syscall;
// v5.4: P_PIDFD idtype support for waitid syscall;
// v5.6: pidfd_getfd syscall.
//
// N.B. Alternative Linux implementations may not follow this ordering. e.g.,
// QEMU user mode 7.2 added pidfd_open, but CLONE_PIDFD was not added until
// 8.0.
package os
import (
_ // for linkname
)
// ensurePidfd initializes the PidFD field in sysAttr if it is not already set.
// It returns the original or modified SysProcAttr struct and a flag indicating
// whether the PidFD should be duplicated before using.
func ensurePidfd( *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
if !pidfdWorks() {
return , false
}
var int
if == nil {
return &syscall.SysProcAttr{
PidFD: &,
}, false
}
if .PidFD == nil {
:= * // copy
.PidFD = &
return &, false
}
return , true
}
// getPidfd returns the value of sysAttr.PidFD (or its duplicate if needDup is
// set) and a flag indicating whether the value can be used.
func getPidfd( *syscall.SysProcAttr, bool) (uintptr, bool) {
if !pidfdWorks() {
return 0, false
}
:= *.PidFD
if {
, := unix.Fcntl(, syscall.F_DUPFD_CLOEXEC, 0)
if != nil {
return 0, false
}
=
}
return uintptr(), true
}
// pidfdFind returns the process handle for pid.
func pidfdFind( int) (uintptr, error) {
if !pidfdWorks() {
return 0, syscall.ENOSYS
}
, := unix.PidFDOpen(, 0)
if != nil {
return 0, convertESRCH()
}
return , nil
}
// pidfdWait waits for the process to complete,
// and updates the process status to done.
func ( *Process) () (*ProcessState, error) {
// When pidfd is used, there is no wait/kill race (described in CL 23967)
// because the PID recycle issue doesn't exist (IOW, pidfd, unlike PID,
// is guaranteed to refer to one particular process). Thus, there is no
// need for the workaround (blockUntilWaitable + sigMu) from pidWait.
//
// We _do_ need to be careful about reuse of the pidfd FD number when
// closing the pidfd. See handle for more details.
, := .handleTransientAcquire()
switch {
case statusDone:
// Process already completed Wait, or was not found by
// pidfdFind. Return ECHILD for consistency with what the wait
// syscall would return.
return nil, NewSyscallError("wait", syscall.ECHILD)
case statusReleased:
return nil, syscall.EINVAL
}
defer .handleTransientRelease()
var (
unix.SiginfoChild
syscall.Rusage
)
:= ignoringEINTR(func() error {
return unix.Waitid(unix.P_PIDFD, int(), &, syscall.WEXITED, &)
})
if != nil {
return nil, NewSyscallError("waitid", )
}
// Update the Process status to statusDone.
// This also releases a reference to the handle.
.doRelease(statusDone)
return &ProcessState{
pid: int(.Pid),
status: .WaitStatus(),
rusage: &,
}, nil
}
// pidfdSendSignal sends a signal to the process.
func ( *Process) ( syscall.Signal) error {
, := .handleTransientAcquire()
switch {
case statusDone:
return ErrProcessDone
case statusReleased:
return errors.New("os: process already released")
}
defer .handleTransientRelease()
return convertESRCH(unix.PidFDSendSignal(, ))
}
// pidfdWorks returns whether we can use pidfd on this system.
func pidfdWorks() bool {
return checkPidfdOnce() == nil
}
// checkPidfdOnce is used to only check whether pidfd works once.
var checkPidfdOnce = sync.OnceValue(checkPidfd)
// checkPidfd checks whether all required pidfd-related syscalls work. This
// consists of pidfd_open and pidfd_send_signal syscalls, waitid syscall with
// idtype of P_PIDFD, and clone(CLONE_PIDFD).
//
// Reasons for non-working pidfd syscalls include an older kernel and an
// execution environment in which the above system calls are restricted by
// seccomp or a similar technology.
func checkPidfd() error {
// In Android version < 12, pidfd-related system calls are not allowed
// by seccomp and trigger the SIGSYS signal. See issue #69065.
if runtime.GOOS == "android" {
ignoreSIGSYS()
defer restoreSIGSYS()
}
// Get a pidfd of the current process (opening of "/proc/self" won't
// work for waitid).
, := unix.PidFDOpen(syscall.Getpid(), 0)
if != nil {
return NewSyscallError("pidfd_open", )
}
defer syscall.Close(int())
// Check waitid(P_PIDFD) works.
= ignoringEINTR(func() error {
var unix.SiginfoChild
// We don't actually care about the info, but passing a nil pointer
// makes valgrind complain because 0x0 is unaddressable.
return unix.Waitid(unix.P_PIDFD, int(), &, syscall.WEXITED, nil)
})
// Expect ECHILD from waitid since we're not our own parent.
if != syscall.ECHILD {
return NewSyscallError("pidfd_wait", )
}
// Check pidfd_send_signal works (should be able to send 0 to itself).
if := unix.PidFDSendSignal(, 0); != nil {
return NewSyscallError("pidfd_send_signal", )
}
// Verify that clone(CLONE_PIDFD) works.
//
// This shouldn't be necessary since pidfd_open was added in Linux 5.3,
// after CLONE_PIDFD in Linux 5.2, but some alternative Linux
// implementations may not adhere to this ordering.
if := checkClonePidfd(); != nil {
return
}
return nil
}
// Provided by syscall.
//
//go:linkname checkClonePidfd
func checkClonePidfd() error
// Provided by runtime.
//
//go:linkname ignoreSIGSYS
func ignoreSIGSYS()
//go:linkname restoreSIGSYS
func restoreSIGSYS()
![]() |
The pages are generated with Golds v0.7.7-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |