Skip to content

Commit 8926ca9

Browse files
ianlancetaylorgopherbot
authored andcommitted
syscall: on exec failure, close pidfd
Fixes #69284 Change-Id: I6350209302778ba5e44fa03d0b9e680d2b4ec192 Reviewed-on: https://go-review.googlesource.com/c/go/+/611495 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: roger peppe <[email protected]> Reviewed-by: Tim King <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent cdca671 commit 8926ca9

File tree

7 files changed

+88
-0
lines changed

7 files changed

+88
-0
lines changed

src/os/pidfd_linux_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"internal/syscall/unix"
1010
"internal/testenv"
1111
"os"
12+
"os/exec"
1213
"syscall"
1314
"testing"
1415
)
@@ -89,3 +90,58 @@ func TestStartProcessWithPidfd(t *testing.T) {
8990
t.Errorf("SendSignal: got %v, want %v", err, syscall.ESRCH)
9091
}
9192
}
93+
94+
// Issue #69284
95+
func TestPidfdLeak(t *testing.T) {
96+
exe := testenv.Executable(t)
97+
98+
// Find the next 10 descriptors.
99+
// We need to get more than one descriptor in practice;
100+
// the pidfd winds up not being the next descriptor.
101+
const count = 10
102+
want := make([]int, count)
103+
for i := range count {
104+
var err error
105+
want[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
106+
if err != nil {
107+
t.Fatal(err)
108+
}
109+
}
110+
111+
// Close the descriptors.
112+
for _, d := range want {
113+
syscall.Close(d)
114+
}
115+
116+
// Start a process 10 times.
117+
for range 10 {
118+
// For testing purposes this has to be an absolute path.
119+
// Otherwise we will fail finding the executable
120+
// and won't start a process at all.
121+
cmd := exec.Command("/noSuchExecutable")
122+
cmd.Run()
123+
}
124+
125+
// Open the next 10 descriptors again.
126+
got := make([]int, count)
127+
for i := range count {
128+
var err error
129+
got[i], err = syscall.Open(exe, syscall.O_RDONLY, 0)
130+
if err != nil {
131+
t.Fatal(err)
132+
}
133+
}
134+
135+
// Close the descriptors
136+
for _, d := range got {
137+
syscall.Close(d)
138+
}
139+
140+
t.Logf("got %v", got)
141+
t.Logf("want %v", want)
142+
143+
// Allow some slack for runtime epoll descriptors and the like.
144+
if got[count-1] > want[count-1]+5 {
145+
t.Errorf("got descriptor %d, want %d", got[count-1], want[count-1])
146+
}
147+
}

src/syscall/exec_bsd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,3 +293,8 @@ childerror:
293293
RawSyscall(SYS_EXIT, 253, 0, 0)
294294
}
295295
}
296+
297+
// forkAndExecFailureCleanup cleans up after an exec failure.
298+
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
299+
// Nothing to do.
300+
}

src/syscall/exec_freebsd.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,8 @@ childerror:
317317
RawSyscall(SYS_EXIT, 253, 0, 0)
318318
}
319319
}
320+
321+
// forkAndExecFailureCleanup cleans up after an exec failure.
322+
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
323+
// Nothing to do.
324+
}

src/syscall/exec_libc.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,11 @@ childerror:
314314
}
315315
}
316316

317+
// forkAndExecFailureCleanup cleans up after an exec failure.
318+
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
319+
// Nothing to do.
320+
}
321+
317322
func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
318323
return ioctl(fd, req, uintptr(arg))
319324
}

src/syscall/exec_libc2.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,8 @@ childerror:
289289
rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0)
290290
}
291291
}
292+
293+
// forkAndExecFailureCleanup cleans up after an exec failure.
294+
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
295+
// Nothing to do.
296+
}

src/syscall/exec_linux.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -750,3 +750,11 @@ func writeUidGidMappings(pid int, sys *SysProcAttr) error {
750750

751751
return nil
752752
}
753+
754+
// forkAndExecFailureCleanup cleans up after an exec failure.
755+
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
756+
if sys.PidFD != nil && *sys.PidFD != -1 {
757+
Close(*sys.PidFD)
758+
*sys.PidFD = -1
759+
}
760+
}

src/syscall/exec_unix.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
237237
for err1 == EINTR {
238238
_, err1 = Wait4(pid, &wstatus, 0, nil)
239239
}
240+
241+
// OS-specific cleanup on failure.
242+
forkAndExecFailureCleanup(attr, sys)
243+
240244
return 0, err
241245
}
242246

0 commit comments

Comments
 (0)