From cdf3249d74c4187bc0c1737e1bb6ab1aa52c0b6f Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Thu, 16 Nov 2023 01:42:39 -0800 Subject: [PATCH] os: make FindProcess use pidfd on Linux Amend FindProcess to use pidfdFind, and make it return ErrProcessDone if pidfdFind is used and the process is not found. Since this is a change in API, introduce GODEBUG osfinderr=0 setting to disable the feature. Change-Id: I724c6f622f0c99f21a70b864cf7cf2b8836869ee Reviewed-on: https://go-review.googlesource.com/c/go/+/542699 LUCI-TryBot-Result: Go LUCI Reviewed-by: Ian Lance Taylor Reviewed-by: Than McIntosh --- doc/godebug.md | 5 +++++ src/internal/godebugs/table.go | 1 + src/os/exec.go | 9 ++++++++- src/os/exec_unix.go | 10 ++++++++-- src/os/pidfd_linux.go | 20 ++++++++++++++++++++ src/os/pidfd_other.go | 4 ++++ src/runtime/metrics/doc.go | 4 ++++ 7 files changed, 50 insertions(+), 3 deletions(-) diff --git a/doc/godebug.md b/doc/godebug.md index a7619c9a3d..9dbbab284d 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -126,6 +126,11 @@ for example, see the [runtime documentation](/pkg/runtime#hdr-Environment_Variables) and the [go command documentation](/cmd/go#hdr-Build_and_test_caching). +### Go 1.23 + +Go 1.23 enabled Linux pidfd support for process lookup. This feature can be +disabled by using the [`osfinderr` setting](/pkg/os#FindProcess). + ### Go 1.22 Go 1.22 adds a configurable limit to control the maximum acceptable RSA key size diff --git a/src/internal/godebugs/table.go b/src/internal/godebugs/table.go index a0a0672966..4ac306b323 100644 --- a/src/internal/godebugs/table.go +++ b/src/internal/godebugs/table.go @@ -42,6 +42,7 @@ var All = []Info{ {Name: "multipartmaxparts", Package: "mime/multipart"}, {Name: "multipathtcp", Package: "net"}, {Name: "netdns", Package: "net", Opaque: true}, + {Name: "osfinderr", Package: "os"}, {Name: "panicnil", Package: "runtime", Changed: 21, Old: "1"}, {Name: "randautoseed", Package: "math/rand"}, {Name: "tarinsecurepath", Package: "archive/tar"}, diff --git a/src/os/exec.go b/src/os/exec.go index 42e8a399a9..7ef1fee595 100644 --- a/src/os/exec.go +++ b/src/os/exec.go @@ -86,10 +86,17 @@ func Getppid() int { return syscall.Getppid() } // The Process it returns can be used to obtain information // about the underlying operating system process. // -// On Unix systems, FindProcess always succeeds and returns a Process +// On Unix systems other than Linux, FindProcess always succeeds and returns a Process // for the given pid, regardless of whether the process exists. To test whether // the process actually exists, see whether p.Signal(syscall.Signal(0)) reports // an error. +// +// On Linux, FindProcess may either return ErrProcessGone for a non-existing +// process (thus eliminating the need to use a signal to check if the process +// exists), or work the same way as for other Unix systems, described above, +// depending on the kernel version used and the system configuration. The old +// behavior (of always succeeding) can be enforced by using GODEBUG setting +// osfinderr=0. func FindProcess(pid int) (*Process, error) { return findProcess(pid) } diff --git a/src/os/exec_unix.go b/src/os/exec_unix.go index 2c66a8be20..21d03da48e 100644 --- a/src/os/exec_unix.go +++ b/src/os/exec_unix.go @@ -106,8 +106,14 @@ func (p *Process) release() error { } func findProcess(pid int) (p *Process, err error) { - // NOOP for unix. - return newProcess(pid, unsetHandle), nil + h, err := pidfdFind(pid) + if err == ErrProcessDone { + return nil, err + } + // Ignore all other errors from pidfdFind, + // as the callers do not expect them, and + // we can use pid anyway. + return newProcess(pid, h), nil } func (p *ProcessState) userTime() time.Duration { diff --git a/src/os/pidfd_linux.go b/src/os/pidfd_linux.go index d6e1d53eee..cc67dfa05f 100644 --- a/src/os/pidfd_linux.go +++ b/src/os/pidfd_linux.go @@ -14,6 +14,7 @@ package os import ( + "internal/godebug" "internal/syscall/unix" "sync" "syscall" @@ -49,6 +50,25 @@ func getPidfd(sysAttr *syscall.SysProcAttr) uintptr { return uintptr(*sysAttr.PidFD) } +var osfinderr = godebug.New("osfinderr") + +func pidfdFind(pid int) (uintptr, error) { + if !pidfdWorks() { + return unsetHandle, syscall.ENOSYS + } + if osfinderr.Value() == "0" { + osfinderr.IncNonDefault() + return unsetHandle, syscall.ENOSYS + + } + + h, err := unix.PidFDOpen(pid, 0) + if err == nil { + return h, nil + } + return unsetHandle, convertESRCH(err) +} + func (p *Process) pidfdRelease() { // Release pidfd unconditionally. handle := p.handle.Swap(unsetHandle) diff --git a/src/os/pidfd_other.go b/src/os/pidfd_other.go index 1918acbec5..bb38c72404 100644 --- a/src/os/pidfd_other.go +++ b/src/os/pidfd_other.go @@ -16,6 +16,10 @@ func getPidfd(_ *syscall.SysProcAttr) uintptr { return unsetHandle } +func pidfdFind(_ int) (uintptr, error) { + return unsetHandle, syscall.ENOSYS +} + func (p *Process) pidfdRelease() {} func (_ *Process) pidfdWait() (*ProcessState, error) { diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go index fb2f44da29..d1c2f8aa25 100644 --- a/src/runtime/metrics/doc.go +++ b/src/runtime/metrics/doc.go @@ -290,6 +290,10 @@ Below is the full list of supported metrics, ordered lexicographically. The number of non-default behaviors executed by the net package due to a non-default GODEBUG=multipathtcp=... setting. + /godebug/non-default-behavior/osfinderr:events + The number of non-default behaviors executed by the os package + due to a non-default GODEBUG=osfinderr=... setting. + /godebug/non-default-behavior/panicnil:events The number of non-default behaviors executed by the runtime package due to a non-default GODEBUG=panicnil=... setting. -- 2.48.1