Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

os: double close pidfd if caller uses pidfd updated by os.StartProcess #68984

Closed
fuweid opened this issue Aug 21, 2024 · 8 comments
Closed

os: double close pidfd if caller uses pidfd updated by os.StartProcess #68984

fuweid opened this issue Aug 21, 2024 · 8 comments
Assignees
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. Critical A critical problem that affects the availability or correctness of production systems built using Go NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@fuweid
Copy link
Contributor

fuweid commented Aug 21, 2024

Go version

go1.23.0

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/fuweid/.cache/go-build'
GOENV='/home/fuweid/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/fuweid/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/fuweid/go'
GOPRIVATE=''
GOPROXY='https://goproxy.cn,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.23.0'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/home/fuweid/.config/go/telemetry'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/fuweid/workspace/demo/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build655447324=/tmp/go-build -gno-record-gcc-switches

What did you do?

NOTE: It requires kernel to support pidfd.

I try to use os.StartProcess with ptrace to remove all the usage of linkname in containerd project containerd/containerd#10611.

For the os.StartProcess option. I set PidFD, use os.NewFile for it and close it.

// PidFD, if not nil, is used to store the pidfd of a child, if the
// functionality is supported by the kernel, or -1. Note *PidFD is
// changed only if the process starts successfully.
PidFD *int

But I run into accept4: bad file descriptor issue randomly, because the Process has finalizer to close the pidfd during GC.

go/src/os/exec_posix.go

Lines 64 to 72 in 6885bad

if runtime.GOOS != "windows" {
var ok bool
h, ok = getPidfd(sysattr.Sys)
if !ok {
return newPIDProcess(pid), nil
}
}
return newHandleProcess(pid, h), nil

go/src/os/exec.go

Lines 114 to 122 in 6885bad

func newHandleProcess(pid int, handle uintptr) *Process {
p := &Process{
Pid: pid,
mode: modeHandle,
handle: handle,
}
p.state.Store(1) // 1 persistent reference
runtime.SetFinalizer(p, (*Process).Release)
return p

There is POC code for reproducing it.

package main

import (
        "fmt"
        "os"
        "runtime"
        "syscall"
        "time"

        "golang.org/x/sys/unix"
)

func main() {
        firstFd := createNoopFd()
        defer firstFd.Close()

        var pidfd int
        proc, err := os.StartProcess("/proc/self/exe", []string{"echo"}, &os.ProcAttr{
                Sys: &syscall.SysProcAttr{
                        Ptrace:    true,
                        PidFD:     &pidfd,
                        Pdeathsig: syscall.SIGKILL,
                },
        })
        if err != nil {
                panic(err)
        }

        if pidfd <= 0 {
                proc.Kill()
                proc.Wait()
                panic("empty pidfd")
        }

        fmt.Println("pidfd = ", pidfd)
        pidFD := os.NewFile(uintptr(pidfd), "pidfd")
        unix.PidfdSendSignal(int(pidFD.Fd()), unix.SIGKILL, nil, 0)
        unix.Waitid(unix.P_PIDFD, int(pidFD.Fd()), nil, unix.WEXITED, nil)
        pidFD.Close()
        fmt.Println("Closed pidfd")

        secondFd := createNoopFd()
        defer secondFd.Close()

        thirdFd := createNoopFd()
        defer thirdFd.Close()

        failedFd := createNoopFd()
        defer failedFd.Close()

        if fd := failedFd.Fd(); fd != uintptr(pidfd) {
                panic("please align fd manually")
        }
        fmt.Println("target fd ", failedFd.Fd())

        for {
                _, err := failedFd.Write([]byte("hello"))
                if err != nil {
                        panic(err)
                }
                time.Sleep(1 * time.Second)
                runtime.GC() // target finalizer for `proc`
        }
}

func createNoopFd() *os.File {
        f, err := os.CreateTemp("/tmp", "PIDFD")
        if err != nil {
                panic(err)
        }
        return f
}

I think the document should mention that the PidFD should not be used or the GO runtime should dup it before using it.

What did you see happen?

When you run the poc code by go1.23.0, you will see the error like.

➜  demo go version
go version go1.23.0 linux/amd64
➜  demo go run main.go
pidfd =  8
Closed pidfd
target fd  8
panic: write /tmp/PIDFD3716412711: bad file descriptor

goroutine 1 [running]:
main.main()
        /home/fuweid/workspace/demo/main.go:59 +0x50b
exit status 2

What did you expect to see?

Should not use user's pidfd value directly.

@fuweid
Copy link
Contributor Author

fuweid commented Aug 21, 2024

cc @kolyshkin

@prattmic
Copy link
Member

I’d say this is a bug. If you manually set PidFD in SysProcAttr, then os.Process shouldn’t close it. That should be your responsibility.

@prattmic
Copy link
Member

cc @golang/runtime

@prattmic prattmic added NeedsFix The path to resolution is known, but the work has not been done. Critical A critical problem that affects the availability or correctness of production systems built using Go labels Aug 21, 2024
@prattmic prattmic added this to the Go1.24 milestone Aug 21, 2024
@prattmic prattmic changed the title os|syscall: double close pidfd if caller uses pidfd updated by os.StartProcess os,syscall: double close pidfd if caller uses pidfd updated by os.StartProcess Aug 21, 2024
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Aug 21, 2024
@gopherbot
Copy link
Contributor

Change https://go.dev/cl/607695 mentions this issue: os: dup pidfd if caller sets PidFD manually

@prattmic
Copy link
Member

@gopherbot Please backport to 1.23. This is a regression in os.StartProcess for programs already using pidfds.

@prattmic prattmic self-assigned this Aug 28, 2024
@gopherbot
Copy link
Contributor

Backport issue(s) opened: #69119 (for 1.23).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://go.dev/wiki/MinorReleases.

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/611415 mentions this issue: [release-branch.go1.23] os: dup pidfd if caller sets PidFD manually

gopherbot pushed a commit that referenced this issue Sep 6, 2024
For #68984.
Fixes #69119.

Change-Id: I16d25777cb38a337cd4204a8147eaf866c3df9e1
Reviewed-on: https://go-review.googlesource.com/c/go/+/607695
Reviewed-by: Kirill Kolyshkin <kolyshkin@gmail.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Commit-Queue: Ian Lance Taylor <iant@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
(cherry picked from commit 239666c)
Reviewed-on: https://go-review.googlesource.com/c/go/+/611415
Auto-Submit: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
TryBot-Bypass: Dmitri Shuralyov <dmitshur@golang.org>
@mknyszek mknyszek changed the title os,syscall: double close pidfd if caller uses pidfd updated by os.StartProcess os: double close pidfd if caller uses pidfd updated by os.StartProcess Oct 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. Critical A critical problem that affects the availability or correctness of production systems built using Go NeedsFix The path to resolution is known, but the work has not been done.
Projects
Development

No branches or pull requests

4 participants