Description
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.
Lines 108 to 111 in 6885bad
But I run into accept4: bad file descriptor
issue randomly, because the Process
has finalizer to close the pidfd during GC.
Lines 64 to 72 in 6885bad
Lines 114 to 122 in 6885bad
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.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status