From 36c0fd66d1eadd15aa25409989630091274bdaa4 Mon Sep 17 00:00:00 2001 From: texasich Date: Tue, 21 Apr 2026 22:05:31 -0500 Subject: [PATCH 1/2] cmdrunner: release process handle in _pidAlive to avoid pidfd leak os.FindProcess on Linux with Go 1.23+ opens a pidfd, and pidWait polls _pidAlive roughly once per second for every plugin process. Without a matching Release the pidfd leaks on each poll, and under Nomad with a few hundred allocations it adds up fast -- one reporter saw it cycle between 20k and 130k open FDs until the process hit EMFILE. Defer proc.Release() right after FindProcess so the handle is closed on every return path. Mirrors the syscall.CloseHandle defer already used in the Windows implementation. Reported downstream in hashicorp/nomad#27847. --- internal/cmdrunner/process_posix.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/cmdrunner/process_posix.go b/internal/cmdrunner/process_posix.go index c9e89411..dcedbb7a 100644 --- a/internal/cmdrunner/process_posix.go +++ b/internal/cmdrunner/process_posix.go @@ -15,6 +15,9 @@ import ( func _pidAlive(pid int) bool { proc, err := os.FindProcess(pid) if err == nil { + // On Linux with Go 1.23+, FindProcess opens a pidfd which must be + // released or it leaks an FD on every call. + defer proc.Release() err = proc.Signal(syscall.Signal(0)) } From 8382575bc978c314b49a49162efb3dbde2dfaca3 Mon Sep 17 00:00:00 2001 From: texasich Date: Tue, 28 Apr 2026 09:19:18 -0500 Subject: [PATCH 2/2] cmdrunner: ignore proc.Release error to satisfy errcheck defer proc.Release() trips the errcheck lint because Release returns an error. The handle is short-lived and there's nothing actionable to recover from a release failure, so swallow the error explicitly to make the intent clear. Per @tgross review on hashicorp/go-plugin#378. Signed-off-by: texasich --- internal/cmdrunner/process_posix.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/cmdrunner/process_posix.go b/internal/cmdrunner/process_posix.go index dcedbb7a..a8a2cab1 100644 --- a/internal/cmdrunner/process_posix.go +++ b/internal/cmdrunner/process_posix.go @@ -16,8 +16,10 @@ func _pidAlive(pid int) bool { proc, err := os.FindProcess(pid) if err == nil { // On Linux with Go 1.23+, FindProcess opens a pidfd which must be - // released or it leaks an FD on every call. - defer proc.Release() + // released or it leaks an FD on every call. Release errors are + // intentionally ignored; the handle is short-lived and there's + // nothing actionable to recover from a release failure. + defer func() { _ = proc.Release() }() err = proc.Signal(syscall.Signal(0)) }