mirror of https://github.com/golang/go.git
212 lines
5.9 KiB
Go
212 lines
5.9 KiB
Go
// 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 (
|
|
"errors"
|
|
"internal/syscall/unix"
|
|
"runtime"
|
|
"sync"
|
|
"syscall"
|
|
_ "unsafe" // 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(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
|
|
if !pidfdWorks() {
|
|
return sysAttr, false
|
|
}
|
|
|
|
var pidfd int
|
|
|
|
if sysAttr == nil {
|
|
return &syscall.SysProcAttr{
|
|
PidFD: &pidfd,
|
|
}, false
|
|
}
|
|
if sysAttr.PidFD == nil {
|
|
newSys := *sysAttr // copy
|
|
newSys.PidFD = &pidfd
|
|
return &newSys, false
|
|
}
|
|
|
|
return sysAttr, 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(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) {
|
|
if !pidfdWorks() {
|
|
return 0, false
|
|
}
|
|
|
|
h := *sysAttr.PidFD
|
|
if needDup {
|
|
dupH, e := unix.Fcntl(h, syscall.F_DUPFD_CLOEXEC, 0)
|
|
if e != nil {
|
|
return 0, false
|
|
}
|
|
h = dupH
|
|
}
|
|
return uintptr(h), true
|
|
}
|
|
|
|
// pidfdFind returns the process handle for pid.
|
|
func pidfdFind(pid int) (uintptr, error) {
|
|
if !pidfdWorks() {
|
|
return 0, syscall.ENOSYS
|
|
}
|
|
|
|
h, err := unix.PidFDOpen(pid, 0)
|
|
if err != nil {
|
|
return 0, convertESRCH(err)
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
// pidfdWait waits for the process to complete,
|
|
// and updates the process status to done.
|
|
func (p *Process) pidfdWait() (*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.
|
|
handle, status := p.handleTransientAcquire()
|
|
switch status {
|
|
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 p.handleTransientRelease()
|
|
|
|
var (
|
|
info unix.SiginfoChild
|
|
rusage syscall.Rusage
|
|
)
|
|
err := ignoringEINTR(func() error {
|
|
return unix.Waitid(unix.P_PIDFD, int(handle), &info, syscall.WEXITED, &rusage)
|
|
})
|
|
if err != nil {
|
|
return nil, NewSyscallError("waitid", err)
|
|
}
|
|
|
|
// Update the Process status to statusDone.
|
|
// This also releases a reference to the handle.
|
|
p.doRelease(statusDone)
|
|
|
|
return &ProcessState{
|
|
pid: int(info.Pid),
|
|
status: info.WaitStatus(),
|
|
rusage: &rusage,
|
|
}, nil
|
|
}
|
|
|
|
// pidfdSendSignal sends a signal to the process.
|
|
func (p *Process) pidfdSendSignal(s syscall.Signal) error {
|
|
handle, status := p.handleTransientAcquire()
|
|
switch status {
|
|
case statusDone:
|
|
return ErrProcessDone
|
|
case statusReleased:
|
|
return errors.New("os: process already released")
|
|
}
|
|
defer p.handleTransientRelease()
|
|
|
|
return convertESRCH(unix.PidFDSendSignal(handle, s))
|
|
}
|
|
|
|
// 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).
|
|
fd, err := unix.PidFDOpen(syscall.Getpid(), 0)
|
|
if err != nil {
|
|
return NewSyscallError("pidfd_open", err)
|
|
}
|
|
defer syscall.Close(int(fd))
|
|
|
|
// Check waitid(P_PIDFD) works.
|
|
err = ignoringEINTR(func() error {
|
|
var info 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(fd), &info, syscall.WEXITED, nil)
|
|
})
|
|
// Expect ECHILD from waitid since we're not our own parent.
|
|
if err != syscall.ECHILD {
|
|
return NewSyscallError("pidfd_wait", err)
|
|
}
|
|
|
|
// Check pidfd_send_signal works (should be able to send 0 to itself).
|
|
if err := unix.PidFDSendSignal(fd, 0); err != nil {
|
|
return NewSyscallError("pidfd_send_signal", err)
|
|
}
|
|
|
|
// 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 err := checkClonePidfd(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Provided by syscall.
|
|
//
|
|
//go:linkname checkClonePidfd
|
|
func checkClonePidfd() error
|
|
|
|
// Provided by runtime.
|
|
//
|
|
//go:linkname ignoreSIGSYS
|
|
func ignoreSIGSYS()
|
|
|
|
//go:linkname restoreSIGSYS
|
|
func restoreSIGSYS()
|