Skip to content

Commit e6598e7

Browse files
fuweidgopherbot
authored andcommitted
[release-branch.go1.23] os: dup pidfd if caller sets PidFD manually
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>
1 parent 82575f7 commit e6598e7

File tree

4 files changed

+58
-12
lines changed

4 files changed

+58
-12
lines changed

src/os/exec_posix.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
3535
}
3636
}
3737

38+
attrSys, shouldDupPidfd := ensurePidfd(attr.Sys)
3839
sysattr := &syscall.ProcAttr{
3940
Dir: attr.Dir,
4041
Env: attr.Env,
41-
Sys: ensurePidfd(attr.Sys),
42+
Sys: attrSys,
4243
}
4344
if sysattr.Env == nil {
4445
sysattr.Env, err = execenv.Default(sysattr.Sys)
@@ -63,7 +64,7 @@ func startProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
6364
// For Windows, syscall.StartProcess above already returned a process handle.
6465
if runtime.GOOS != "windows" {
6566
var ok bool
66-
h, ok = getPidfd(sysattr.Sys)
67+
h, ok = getPidfd(sysattr.Sys, shouldDupPidfd)
6768
if !ok {
6869
return newPIDProcess(pid), nil
6970
}

src/os/pidfd_linux.go

+20-7
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,46 @@ import (
1919
"unsafe"
2020
)
2121

22-
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
22+
// ensurePidfd initializes the PidFD field in sysAttr if it is not already set.
23+
// It returns the original or modified SysProcAttr struct and a flag indicating
24+
// whether the PidFD should be duplicated before using.
25+
func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
2326
if !pidfdWorks() {
24-
return sysAttr
27+
return sysAttr, false
2528
}
2629

2730
var pidfd int
2831

2932
if sysAttr == nil {
3033
return &syscall.SysProcAttr{
3134
PidFD: &pidfd,
32-
}
35+
}, false
3336
}
3437
if sysAttr.PidFD == nil {
3538
newSys := *sysAttr // copy
3639
newSys.PidFD = &pidfd
37-
return &newSys
40+
return &newSys, false
3841
}
3942

40-
return sysAttr
43+
return sysAttr, true
4144
}
4245

43-
func getPidfd(sysAttr *syscall.SysProcAttr) (uintptr, bool) {
46+
// getPidfd returns the value of sysAttr.PidFD (or its duplicate if needDup is
47+
// set) and a flag indicating whether the value can be used.
48+
func getPidfd(sysAttr *syscall.SysProcAttr, needDup bool) (uintptr, bool) {
4449
if !pidfdWorks() {
4550
return 0, false
4651
}
4752

48-
return uintptr(*sysAttr.PidFD), true
53+
h := *sysAttr.PidFD
54+
if needDup {
55+
dupH, e := unix.Fcntl(h, syscall.F_DUPFD_CLOEXEC, 0)
56+
if e != nil {
57+
return 0, false
58+
}
59+
h = dupH
60+
}
61+
return uintptr(h), true
4962
}
5063

5164
func pidfdFind(pid int) (uintptr, error) {

src/os/pidfd_linux_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package os_test
66

77
import (
88
"errors"
9+
"internal/syscall/unix"
910
"internal/testenv"
1011
"os"
1112
"syscall"
@@ -57,3 +58,34 @@ func TestFindProcessViaPidfd(t *testing.T) {
5758
t.Fatalf("Release: got %v, want <nil>", err)
5859
}
5960
}
61+
62+
func TestStartProcessWithPidfd(t *testing.T) {
63+
testenv.MustHaveGoBuild(t)
64+
t.Parallel()
65+
66+
if err := os.CheckPidfdOnce(); err != nil {
67+
// Non-pidfd code paths tested in exec_unix_test.go.
68+
t.Skipf("skipping: pidfd not available: %v", err)
69+
}
70+
71+
var pidfd int
72+
p, err := os.StartProcess(testenv.GoToolPath(t), []string{"go"}, &os.ProcAttr{
73+
Sys: &syscall.SysProcAttr{
74+
PidFD: &pidfd,
75+
},
76+
})
77+
if err != nil {
78+
t.Fatalf("starting test process: %v", err)
79+
}
80+
defer syscall.Close(pidfd)
81+
82+
if _, err := p.Wait(); err != nil {
83+
t.Fatalf("Wait: got %v, want <nil>", err)
84+
}
85+
86+
// Check the pidfd is still valid
87+
err = unix.PidFDSendSignal(uintptr(pidfd), syscall.Signal(0))
88+
if !errors.Is(err, syscall.ESRCH) {
89+
t.Errorf("SendSignal: got %v, want %v", err, syscall.ESRCH)
90+
}
91+
}

src/os/pidfd_other.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ package os
88

99
import "syscall"
1010

11-
func ensurePidfd(sysAttr *syscall.SysProcAttr) *syscall.SysProcAttr {
12-
return sysAttr
11+
func ensurePidfd(sysAttr *syscall.SysProcAttr) (*syscall.SysProcAttr, bool) {
12+
return sysAttr, false
1313
}
1414

15-
func getPidfd(_ *syscall.SysProcAttr) (uintptr, bool) {
15+
func getPidfd(_ *syscall.SysProcAttr, _ bool) (uintptr, bool) {
1616
return 0, false
1717
}
1818

0 commit comments

Comments
 (0)